mirror of
https://github.com/XRPLF/xrpl-dev-portal.git
synced 2026-06-07 10:46:45 +00:00
Compare commits
285 Commits
feat/new-e
...
feat/docsP
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2df0d5041f | ||
|
|
b487e2e72a | ||
|
|
66cc13c815 | ||
|
|
248ae1aa2f | ||
|
|
2e7b4ce758 | ||
|
|
30c3a24204 | ||
|
|
7933e3c2f7 | ||
|
|
5c854bd2e8 | ||
|
|
df6a1587f1 | ||
|
|
af81994d9c | ||
|
|
76c5d6a88d | ||
|
|
f443c67a21 | ||
|
|
8d65b8e751 | ||
|
|
339bd84540 | ||
|
|
25760d1ef8 | ||
|
|
c714657c62 | ||
|
|
5e19f0e01c | ||
|
|
3c86f7ac29 | ||
|
|
7d2ea6110e | ||
|
|
b7db009786 | ||
|
|
97bb0f13c9 | ||
|
|
74e6d20ec5 | ||
|
|
06de729eba | ||
|
|
3f1d5a148e | ||
|
|
8498fef70e | ||
|
|
f9d84f916d | ||
|
|
3ff8607cc5 | ||
|
|
4e3a6f322b | ||
|
|
2a3746eb07 | ||
|
|
35571903e0 | ||
|
|
a89fcbb275 | ||
|
|
0dcab2ccd1 | ||
|
|
10b3b7f081 | ||
|
|
79e7cee695 | ||
|
|
8eb5173ebc | ||
|
|
2acc7c4213 | ||
|
|
e7904a0f0f | ||
|
|
68f10b41ad | ||
|
|
3e6dd8687d | ||
|
|
84109910b2 | ||
|
|
8f0db49832 | ||
|
|
32e89c1299 | ||
|
|
a97a009d93 | ||
|
|
df074e625b | ||
|
|
a4b1925b31 | ||
|
|
26d1cf102c | ||
|
|
a41a9e31cc | ||
|
|
159ac52acc | ||
|
|
c7e01d322a | ||
|
|
17582d543d | ||
|
|
986ca23ff7 | ||
|
|
acb2476d7d | ||
|
|
f5c38ffe77 | ||
|
|
ee6a32d159 | ||
|
|
e1d18bd621 | ||
|
|
a85dc47781 | ||
|
|
eecd14d763 | ||
|
|
42282a2012 | ||
|
|
6442318205 | ||
|
|
1ee76bfbea | ||
|
|
d558b7474d | ||
|
|
da49b0a154 | ||
|
|
01931bd177 | ||
|
|
c2ef761b01 | ||
|
|
d6ce246420 | ||
|
|
33c6315510 | ||
|
|
af0b8cd40a | ||
|
|
95c4ffaa1b | ||
|
|
08c5572f16 | ||
|
|
b49bc02dd2 | ||
|
|
65a61c5e47 | ||
|
|
237ddc3c74 | ||
|
|
dd6cfd34fe | ||
|
|
607f8bdf07 | ||
|
|
7b601da3a0 | ||
|
|
dc85b8c241 | ||
|
|
0a25b1b9c0 | ||
|
|
6021b458e6 | ||
|
|
e5f3bf75e3 | ||
|
|
7dd32d63da | ||
|
|
7376dce9ef | ||
|
|
ce9012f26f | ||
|
|
9042a60b28 | ||
|
|
9ff586b172 | ||
|
|
a082d9030c | ||
|
|
9814a24dcf | ||
|
|
05c36beae2 | ||
|
|
41e01b51bd | ||
|
|
4c2b2d487c | ||
|
|
dec76b1f71 | ||
|
|
b26b185c04 | ||
|
|
fb7707c6b6 | ||
|
|
ee80283265 | ||
|
|
a3119f9fc0 | ||
|
|
5c87e7e1cb | ||
|
|
9084d37db3 | ||
|
|
bf7bd6fbd7 | ||
|
|
50df631f8a | ||
|
|
0d779b4d47 | ||
|
|
071e940e6b | ||
|
|
9fe2a7377f | ||
|
|
e40ea50259 | ||
|
|
41e3e82984 | ||
|
|
1ba6c9753d | ||
|
|
0fe57025c6 | ||
|
|
e43ce195a4 | ||
|
|
c192ccec70 | ||
|
|
8a2ff6e69f | ||
|
|
6bce7efae0 | ||
|
|
df1ab88ef7 | ||
|
|
8f931a2a4c | ||
|
|
be46c362cf | ||
|
|
99d3442bef | ||
|
|
e66a877868 | ||
|
|
b9410305ef | ||
|
|
d5e7fceb21 | ||
|
|
7be7ad4806 | ||
|
|
7498f9820c | ||
|
|
9d4ed9a477 | ||
|
|
7a6aab6493 | ||
|
|
a992f0ddf3 | ||
|
|
87c3c6ef19 | ||
|
|
8c5f6f79c1 | ||
|
|
a6fde81c36 | ||
|
|
b085502a4d | ||
|
|
4da20f1ac1 | ||
|
|
ef200ee737 | ||
|
|
9b05da7131 | ||
|
|
f7c80a5c04 | ||
|
|
1e61c71c94 | ||
|
|
bf88924d3d | ||
|
|
daa8b7d292 | ||
|
|
3873ae0085 | ||
|
|
f630796da0 | ||
|
|
eac1859507 | ||
|
|
2d75a0a727 | ||
|
|
f62c99b387 | ||
|
|
7383bf8044 | ||
|
|
e12b1bf8dc | ||
|
|
0b52e5f747 | ||
|
|
161e4305e6 | ||
|
|
8e7d7ecba1 | ||
|
|
32f6a1de2d | ||
|
|
e3459b336e | ||
|
|
1a1e1b30a6 | ||
|
|
5eb98cacac | ||
|
|
9a7f9479d4 | ||
|
|
c21785855f | ||
|
|
887e1f38f5 | ||
|
|
79294acb05 | ||
|
|
5cb06eaf86 | ||
|
|
fe0057aa9f | ||
|
|
1e3ca30ace | ||
|
|
66356984b4 | ||
|
|
1fcc294ffb | ||
|
|
0d0fc38344 | ||
|
|
cd1759332d | ||
|
|
62d23ce36b | ||
|
|
678e168029 | ||
|
|
31a9cac20b | ||
|
|
79f40fb2c6 | ||
|
|
9738402921 | ||
|
|
e064ce02d0 | ||
|
|
862a5c42d8 | ||
|
|
d29a5083d1 | ||
|
|
60fc8eb22e | ||
|
|
ec4ef6e9fc | ||
|
|
42552e4d24 | ||
|
|
e46e4006d5 | ||
|
|
5cf22174dc | ||
|
|
7fd39abb2b | ||
|
|
d7e042bdb6 | ||
|
|
9006dc3812 | ||
|
|
f3bef3784f | ||
|
|
b8286bf6b4 | ||
|
|
3feb69a1da | ||
|
|
e94be3ca20 | ||
|
|
4776c45c33 | ||
|
|
1278b1aca9 | ||
|
|
da529fd71e | ||
|
|
e467e27448 | ||
|
|
de84fa25a8 | ||
|
|
b4ddfa7955 | ||
|
|
ce75b4388c | ||
|
|
46add22436 | ||
|
|
93ca38ed76 | ||
|
|
e0430b9899 | ||
|
|
5df5f38e83 | ||
|
|
2b15495835 | ||
|
|
ab366c79ef | ||
|
|
1074670da7 | ||
|
|
1a2cb105f3 | ||
|
|
3badef78c1 | ||
|
|
a1f4c82e3a | ||
|
|
478f5784ee | ||
|
|
471bf7f193 | ||
|
|
f346a80ce0 | ||
|
|
e849cc95b9 | ||
|
|
0bff1aab4c | ||
|
|
e6aa704841 | ||
|
|
9415ae085a | ||
|
|
4cb232a068 | ||
|
|
35958ebede | ||
|
|
688ac5dc91 | ||
|
|
e298a45902 | ||
|
|
176e187c6a | ||
|
|
15046f431e | ||
|
|
ecd4a1bb66 | ||
|
|
3fbed79209 | ||
|
|
fc472a4f77 | ||
|
|
bebc019daa | ||
|
|
b3f235ded6 | ||
|
|
7b223aafc2 | ||
|
|
ffe0eff61a | ||
|
|
be0e324d0b | ||
|
|
fadfde1775 | ||
|
|
32899e9c41 | ||
|
|
15f48991c3 | ||
|
|
642c0dd2ce | ||
|
|
a08b24ed5d | ||
|
|
74e8be5a13 | ||
|
|
7895d6dee9 | ||
|
|
8e6d8f7c30 | ||
|
|
36785bc0f1 | ||
|
|
977c37ef83 | ||
|
|
52d895b1d0 | ||
|
|
230ddcbe21 | ||
|
|
1ee5828747 | ||
|
|
314980a667 | ||
|
|
2783d90cf6 | ||
|
|
5fbdbb8d42 | ||
|
|
b7ba976fb2 | ||
|
|
a6eb9e63e5 | ||
|
|
7d694c76a5 | ||
|
|
cbc56937e6 | ||
|
|
1a9fa9b970 | ||
|
|
cd82ea5484 | ||
|
|
57898ab010 | ||
|
|
2dbb111943 | ||
|
|
cb6323d153 | ||
|
|
08941588aa | ||
|
|
e183369ef6 | ||
|
|
702e180de6 | ||
|
|
a265f82980 | ||
|
|
021899906d | ||
|
|
f022c48f6c | ||
|
|
3e07b8400d | ||
|
|
5433894f20 | ||
|
|
a6d84de417 | ||
|
|
6f76d4ece5 | ||
|
|
17778ad84b | ||
|
|
518585227d | ||
|
|
ad0631f701 | ||
|
|
01c19628a9 | ||
|
|
44614dba9d | ||
|
|
621db81c7d | ||
|
|
c01749eba2 | ||
|
|
2ff14e4224 | ||
|
|
7685c2eb1e | ||
|
|
2de2bac211 | ||
|
|
bdc69f047a | ||
|
|
f20177b5f9 | ||
|
|
73b2127f87 | ||
|
|
32b309c878 | ||
|
|
9cf1b07954 | ||
|
|
5b73ccb8be | ||
|
|
c4188c47d6 | ||
|
|
2429574182 | ||
|
|
42ec50df27 | ||
|
|
37e96a9dae | ||
|
|
f3ae760c40 | ||
|
|
97c302822a | ||
|
|
e92929e148 | ||
|
|
9d3d11800a | ||
|
|
a956d5ae78 | ||
|
|
52e070dcf6 | ||
|
|
605eb70aed | ||
|
|
0c2a1bc249 | ||
|
|
51e763b967 | ||
|
|
86998c82d6 | ||
|
|
c2287a7fe6 | ||
|
|
f09ab44280 | ||
|
|
08807db2e9 | ||
|
|
201479ced6 | ||
|
|
ce49c8b6ba |
262
.claude/commands/figma-build-page.md
Normal file
262
.claude/commands/figma-build-page.md
Normal file
@@ -0,0 +1,262 @@
|
||||
# Figma Build Page
|
||||
|
||||
Build a `.page.tsx` file from a Figma design URL by mapping each Figma section to an existing shared section component, and export any new images/icons from Figma into the project's asset folders.
|
||||
|
||||
**Usage:** `/figma-build-page <figma-url> <target-page-file>`
|
||||
|
||||
**Example:** `/figma-build-page https://www.figma.com/design/7vhTxPgH1in2AjIETuX49Z/Documentation?node-id=0-1 docs/index.page.tsx`
|
||||
|
||||
---
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1 — Parse the input
|
||||
|
||||
Extract from `$ARGUMENTS`:
|
||||
- **Figma URL**: extract `fileKey` and `nodeId` (convert `-` to `:` in nodeId)
|
||||
- **Target file**: the `.page.tsx` file path to rebuild
|
||||
- **Page slug**: derive from the target file path (e.g. `docs/index.page.tsx` → `docs`, `resources/about.page.tsx` → `resources-about`). This is used to prefix shared media filenames.
|
||||
|
||||
### Step 2 — Understand the design structure
|
||||
|
||||
Run in parallel:
|
||||
1. Read the current target page file to understand what already exists
|
||||
2. Run `find shared/sections -name "*.tsx"` and `find shared/components -name "*.tsx"` to inventory available components
|
||||
3. Run `find static/img/icons/2026 static/img/logos/black static/img/bds-2026 -type f` to inventory existing assets so the build can reuse them instead of re-exporting duplicates
|
||||
4. Call `mcp__claude_ai_Figma__get_metadata` on the **page root** (the parent of the `nodeId` you were given) — pass `nodeId="0:1"` if you don't know the page id, otherwise walk up one level from the given node. The goal is to see ALL sibling frames on the canvas, not just the LG/MD/SM page mockups. Section frames alone are not enough — designers also park reference data in sibling frames.
|
||||
|
||||
#### Step 2A — Locate the Assets frame (REQUIRED, do this before anything else design-side)
|
||||
|
||||
Designers park clean, isolated versions of the page's icons, logos, photos, and **canonical card/list content** in a sibling frame on the same canvas — typically named `Assets`, `Assets Library`, `Exports`, `Resources`, `Content`, or similar. It lives outside the main `LG`/`MD`/`SM` page frames, usually far to the right or below them.
|
||||
|
||||
From the page-root metadata, find any frame whose name matches `/asset|export|library|resource|content/i` OR whose x/y position places it clearly outside the LG/MD/SM stack. Record:
|
||||
- Its `nodeId` and name
|
||||
- Every child node's `nodeId`, layer name, and visual role (icon vs photo vs logo vs card content)
|
||||
|
||||
**Then immediately call `mcp__claude_ai_Figma__get_design_context` on the Assets frame** to extract its contents — child layer names, image asset URLs, and any text labels. Do this BEFORE fetching the section contexts in Step 3, because the Assets frame is the source of truth for:
|
||||
- **Real partner/customer logos** — the LG frame typically shows placeholder logos (e.g. the same logo repeated 8×); the Assets frame holds the real brand SVGs.
|
||||
- **Full carousel card sets** — the LG frame may only show 3-4 visible slides (with duplicates as overflow placeholders); the Assets frame holds the full N-card list with no duplicates.
|
||||
- **Canonical icon artwork** — the icon inside a section is often a low-fidelity instance of the master icon in the Assets frame.
|
||||
- **Per-card hrefs, hidden subtitles, alt text, video URLs** — content the section mockup doesn't have room to show.
|
||||
|
||||
Build an **Assets Index**: `{ layerName, nodeId, role, imageAssetUrl?, associatedSectionHint? }` for every child. Use the layer name to associate each asset with a section (e.g. layer `RWA Partner — Archax` belongs in the logo grid; layer `Carousel Card 04 — Onchain Trading` belongs in the carousel).
|
||||
|
||||
**If the page also has annotations pointing at the Assets frame** (e.g. `data-annotations="Logo - see right side asset frame"`, `"Full list in Assets"`, `"Cards continue in Assets frame"`), treat that as a hard contract: the Assets frame is the authoritative content for that section, and the LG mockup is just a visual stub.
|
||||
|
||||
**Fail-loud if no Assets frame is found:** if you can't find a sibling frame and the page has more than one media/logo/icon, surface this to the user before continuing — e.g. "I didn't find an Assets/Exports frame on this canvas. The LG mockup shows N placeholder logos and a 3-card carousel — proceed treating those as the final content, or point me at the Assets frame?" Don't silently fall back to the LG mockup when the design clearly references external content.
|
||||
|
||||
### Step 3 — Fetch design context for each section (cross-reference with Assets frame)
|
||||
|
||||
For every top-level section frame found in the metadata, call `mcp__claude_ai_Figma__get_design_context` in parallel batches (max 3 at a time). For each section, gather:
|
||||
- The heading/title text
|
||||
- Any body/description text
|
||||
- All link labels and their annotation `href` values (from `data-annotations`)
|
||||
- Color variants visible (background color tells you which variant to use)
|
||||
- The arrangement (left/right, content vs media position)
|
||||
- Every distinct image/icon node — capture its Figma `nodeId`, layer name, fill color, and visual role (icon vs photo, monochrome vs colored)
|
||||
|
||||
**Then cross-reference against the Assets Index from Step 2A:**
|
||||
- For carousels/lists: if the LG mockup shows fewer cards than the Assets frame, USE THE ASSETS-FRAME COUNT and content. If cards in the mockup look like exact duplicates (same icon + same text), treat them as overflow placeholders and drop them in favor of the Assets-frame set.
|
||||
- For logo grids: if the mockup repeats the same logo, IGNORE that and use the distinct logos from the Assets frame.
|
||||
- For media: if the section's media node and an Assets-frame node share a layer name or visible image, prefer the Assets-frame node — it's higher fidelity and isolated from section chrome.
|
||||
- For icons: if a section icon has a matching layer name in the Assets frame, export from the Assets frame, not the section instance.
|
||||
|
||||
**Important:** Note any Figma annotations that say content should be removed or changed, OR that redirect to the Assets frame ("see right side asset frame", "real logos in assets", etc.) — annotations override what the mockup shows.
|
||||
|
||||
### Step 4 — Map sections to existing components
|
||||
|
||||
For each Figma section, find the best matching component from `shared/sections/`. Common mappings:
|
||||
|
||||
| Figma Section Name | Component | Import |
|
||||
|---|---|---|
|
||||
| `headerHeroPrimaryMedia` | `HeaderHeroPrimaryMedia` (default export) | `shared/sections/HeaderHeroPrimaryMedia/HeaderHeroPrimaryMedia` |
|
||||
| `CarouselCardListOffGrid` | `CarouselCardList` | `shared/sections/CarouselCardList/CarouselCardList` |
|
||||
| `TextGridCard` | `CardsTextGrid` | `shared/sections/CardsTextGrid/CardsTextGrid` |
|
||||
| `VideoFeatured` | `FeaturedVideoHero` (default export) | `shared/sections/FeaturedVideoHero/FeaturedVideoHero` |
|
||||
| `LinkSmallGrid` | `LinkSmallGrid` | `shared/sections/LinkSmallGrid/LinkSmallGrid` |
|
||||
| `LinkTextDirectory` | `LinkTextDirectory` | `shared/sections/LinkTextDirectory/LinkTextDirectory` |
|
||||
| `FeatureTwoColumn` | `FeatureTwoColumn` | `shared/sections/FeatureTwoColumn/FeatureTwoColumn` |
|
||||
| `LinkSmallTiles` | `SmallTilesSection` | `shared/sections/SmallTilesSection/SmallTilesSection` |
|
||||
| `IconTextGridCard` | `CardsIconGrid` | `shared/sections/CardsIconGrid/CardsIconGrid` |
|
||||
| `StandardCard` group | `StandardCardGroupSection` | `shared/sections/StandardCardGroupSection/StandardCardGroupSection` |
|
||||
|
||||
Read the prop interfaces of each matched component before writing (`head -60` of the component file is usually enough).
|
||||
|
||||
**Key prop notes:**
|
||||
- `HeaderHeroPrimaryMedia` / `FeaturedVideoHero` — **default exports**; all others are named exports
|
||||
- `FeaturedVideoHero` video source: `{ type: 'embed', embedUrl: 'https://www.youtube.com/embed/VIDEO_ID' }`
|
||||
- `FeatureTwoColumn` `description` prop is required — pass `""` if the Figma shows no body text
|
||||
- `LinkTextDirectory` buttons use `ButtonConfig`: `{ label: string; href?: string }`
|
||||
- `StandardCardGroupSection` callsToAction uses `DesignConstrainedButtonProps`: `{ children: ReactNode; href?: string }`
|
||||
- `CardsTextGrid` / `CardsIconGrid` `description` field accepts `React.ReactNode` — use this for inline links
|
||||
- `SmallTilesSection` uses existing logo assets: `require('../static/img/logos/black/javascript.svg')` etc.
|
||||
|
||||
### Step 5 — Determine color variants from Figma
|
||||
|
||||
Match Figma background colors to component variant props:
|
||||
- White / `#ffffff` → `"neutral"` or `"gray"` (default)
|
||||
- Green `#70ee97` / `#21e46b` → `"green"`
|
||||
- Lilac/purple `#d9caff` → `"lilac"`
|
||||
- Yellow → `"yellow"`
|
||||
|
||||
### Step 6 — Plan asset exports
|
||||
|
||||
For every image/icon node identified in Step 3, decide:
|
||||
|
||||
**A) Source node — always prefer the Assets frame:** For each asset slot in the page, look up the matching node in the Assets Index from Step 2A by layer name or visual match. **Export from the Assets-frame node, not the section instance.** Section instances are often scaled-down thumbnails or shared placeholders; the Assets frame holds the master artwork at its intended export size. Only fall back to the section instance if the Assets frame genuinely doesn't have a counterpart.
|
||||
|
||||
**B) Reuse vs export:** If a visually-equivalent asset already exists in the inventory from Step 2 (same role, same color), reuse its path and skip export. Otherwise schedule a new export.
|
||||
|
||||
**C) Target folder** — route by section type and asset role:
|
||||
|
||||
| Section / role | Folder | Format |
|
||||
|---|---|---|
|
||||
| `CarouselCardList` card icons (monochrome glyphs) | `static/img/icons/2026/black/` | `.svg` |
|
||||
| `CardsIconGrid` card icons (colored glyphs) | `static/img/icons/2026/color/<color>/` | `.svg` |
|
||||
| `SmallTilesSection` card icons (SDK / language logos) | `static/img/logos/black/` | `.svg` |
|
||||
| `LogoRectangleGrid` partner logos (rendered from inner `Logo` frame, 1.5× scale) | `static/img/logos/black/` | `.png` |
|
||||
| `HeaderHeroPrimaryMedia` hero media (photo) | `static/img/bds-2026/` | `.jpg` (composite raw asset onto Figma bg color at native size, see Step 7) |
|
||||
| `HeaderHeroSplitMedia` hero media (photo) | `static/img/bds-2026/` | `.jpg` (composite raw asset onto Figma bg color at native size, see Step 7) |
|
||||
| `FeatureTwoColumn` media (photo) | `static/img/bds-2026/` | `.jpg` (composite raw asset onto Figma bg color at native size, see Step 7) |
|
||||
| `FeatureSingleTopic` media (photo) | `static/img/bds-2026/` | `.jpg` (composite raw asset onto Figma bg color at native size, see Step 7) |
|
||||
| `CardsFeatured` / `logoRectangleGrid`-as-image-cards card media | `static/img/bds-2026/` | `.jpg` (screenshot card-image frame at 1.5×, see Step 7) |
|
||||
| `FeaturedVideoHero` poster (if used) | `static/img/bds-2026/` | `.jpg` (composite raw asset onto Figma bg color at native size, see Step 7) |
|
||||
| `CarouselFeatured` slide HeroMedia (photo) | `static/img/bds-2026/` | `.jpg` (composite raw asset onto Figma bg color at native size, see Step 7) |
|
||||
| Any other colored decorative icon | `static/img/icons/2026/color/<color>/` | `.svg` |
|
||||
| Any other monochrome icon | `static/img/icons/2026/black/` | `.svg` |
|
||||
|
||||
For the colored-icon folders, pick `<color>` from the Figma fill on the icon (or, if the icon inherits color from a parent component, the parent section's variant prop). Existing folders: `lilac`. Create a new sibling folder (`green`, `yellow`, etc.) if the design uses a new color — never dump differently-colored icons into the wrong folder.
|
||||
|
||||
**D) Filename — always kebab-case:**
|
||||
|
||||
1. Start from the Figma layer name.
|
||||
2. Lowercase, replace spaces / underscores / camelCase boundaries with `-`, strip non-`[a-z0-9-]`.
|
||||
3. For shared photographic media in `bds-2026/`, **prefix with the page slug** to avoid cross-page collisions:
|
||||
- hero media → `<page-slug>-hero-media.jpg`
|
||||
- feature media (multi) → `<page-slug>-feature-media-1.jpg`, `<page-slug>-feature-media-2.jpg`, …
|
||||
4. Icons and SDK logos are not page-prefixed — they're meant to be reusable across pages.
|
||||
|
||||
Examples:
|
||||
- Figma layer `Ready to Use Code Samples` (CarouselCardList icon) → `static/img/icons/2026/black/ready-to-use-code-samples.svg`
|
||||
- Figma layer `XRPL Server` (CardsIconGrid icon, lilac fill) → `static/img/icons/2026/color/lilac/xrpl-server.svg`
|
||||
- Figma layer `Hero Image` on `docs/index.page.tsx` → `static/img/bds-2026/docs-hero-media.jpg`
|
||||
- Figma layer `Feature 02` on `resources/about.page.tsx` → `static/img/bds-2026/resources-about-feature-media-2.jpg`
|
||||
- Figma frame `Logo_Circle` → `Logo` (inner, LogoRectangleGrid partner) → `static/img/logos/black/circle.png` (1.5× PNG, see Step 7)
|
||||
- Figma frame `Logo_Ondo` → `Logo` (inner, LogoRectangleGrid partner) → `static/img/logos/black/ondo.png`
|
||||
|
||||
### Step 7 — Export the assets from Figma
|
||||
|
||||
For each scheduled export from Step 6 (sourced from the Assets frame per Step 6A whenever possible):
|
||||
|
||||
1. Ensure the target folder exists (`mkdir -p` it if not).
|
||||
2. **Get the raw Figma asset URL.** Calling `mcp__claude_ai_Figma__get_design_context` on a frame returns inline TS that declares `const imgXxx = "https://www.figma.com/api/mcp/asset/<uuid>"` constants — one per leaf image/icon node. These URLs serve the **raw underlying asset** (SVG for vector icons, PNG for photos/rasters), NOT a re-rendered screenshot of the surrounding chrome. Pull the URL for the specific Assets-frame child you mapped to this slot.
|
||||
3. **Download with `curl -sL`** for raw vector assets that render as-is with no framing chrome — typically SVG icons and SDK/language logos:
|
||||
- SVG icons / vector logos → `curl -sL <url> -o <target>.svg` — Figma serves the actual SVG markup for vector nodes, so no conversion is needed.
|
||||
- Verify the format with `file <path>` after downloading the first one of each type, in case Figma serves something unexpected.
|
||||
|
||||
3a. **Photographic media with backgrounds — composite the raw image onto the design's background color, save as JPG at native size.** Most media nodes in the design system (hero media, FeatureTwoColumn media, FeatureSingleTopic media, FeaturedVideoHero poster, CardsFeatured card image, CarouselFeatured slide hero) live inside a wrapper frame named `HeroMedia` / `FeatureMedia` / similar. In Figma, that wrapper frame may have a background fill (e.g. `bg-neutral/100` = `#f0f3f7`, `bg-neutral/200` = `#e6eaf0`) BEHIND a foreground illustration. The raw `imgXxx` URL gives you the foreground image only — strips the background, leaves transparent edges. When the JPG converter then flattens to white, the image looks "floating on white" instead of "sitting on the gray tile" the designer intended.
|
||||
|
||||
To capture the composited image + background fill as a flat JPG **at the source image's native size** (no upscale):
|
||||
|
||||
1. **Pull the asset URL from the Assets frame.** This is the priority. Calling `get_design_context` on the Assets frame returns the master `imgXxx = "https://www.figma.com/api/mcp/asset/<uuid>"` constants — clean, isolated, full-size. Only fall back to the section instance's asset URL when the Assets frame has no counterpart.
|
||||
|
||||
2. **Inspect the design context to find the background color** the image sits on. Look at the wrapper frame around the `<img>` for a sibling/child div with `bg-[var(--neutral\/100,#f0f3f7)]`, `bg-[var(--neutral\/200,#e6eaf0)]`, or similar. If there's no bg div, use `#ffffff`.
|
||||
|
||||
3. **Download the raw asset:** `curl -sL <imgXxx-url> -o /tmp/<name>.png`. Most photo nodes return PNG with alpha (illustrations / partial photos); some return JPEG (full-bleed photos with no alpha).
|
||||
|
||||
4. **Composite onto the background color and save as JPG using ImageMagick** (install once with `brew install imagemagick`):
|
||||
```
|
||||
magick /tmp/<name>.png -background "<hex>" -alpha remove -alpha off -quality 85 <target>.jpg
|
||||
```
|
||||
**No `-resize` flag** — preserve the source's native dimensions. (For very large source PNGs above ~2000px, you may add `-resize "1500x1500>"` to cap size for web, but otherwise leave the native size alone — the designer chose it.) The `-alpha remove -alpha off` flattens alpha onto the `-background` color BEFORE the JPEG encoder strips alpha to white, giving the correct background instead of white.
|
||||
|
||||
5. Save with the `<page-slug>-feature-media-N.jpg` (or `-hero-media.jpg`, `-video-poster.jpg`, etc.) naming convention from Step 6.
|
||||
|
||||
**DO NOT use `get_screenshot` on the media frame** — instance children inside design-system components have IDs like `I<sectionId>;<...>` which are not screenshotable, and the MCP rejects them. The composite-from-raw-asset approach above works for all cases.
|
||||
|
||||
**Sanity-check the first one.** After exporting the first media of each kind, eyeball the JPG: the foreground illustration should sit on a uniform colored tile (`#f0f3f7` light gray, `#e6eaf0` slightly darker gray, etc.) — NOT on white when the design called for gray. If it's still white-flat, you used the wrong `-background` hex; re-read the Figma frame's `bg-` value.
|
||||
4. **LogoRectangleGrid partner logos — special procedure.** Multi-layer brand logos (Circle, Zeconomy, DB Schenker, etc.) decompose into many vector pieces under `get_design_context`, so the raw-asset-URL technique above gives you fragments, not a composite. Use this instead:
|
||||
1. Target the **inner `Logo` frame** (the 170×96 child of `Logo_<Name>`), not the gray-tile parent (`Logo_<Name>`) and not the bottom-most leaf layer (e.g. `Ondo finance`, `DB Schenker_Logo_0 1`).
|
||||
2. Call `mcp__claude_ai_Figma__get_screenshot` on that inner Logo frame with `contentsOnly: true` and `maxDimension: 256` to request the longer-edge at ~1.5× the native 170 px. Note that Figma's MCP does not supersample beyond a frame's native render size, so the returned PNG is typically still 170×96 — that's expected, not an error.
|
||||
3. Download the PNG: `curl -sL <image_url> -o /tmp/<name>.png`.
|
||||
4. Upscale to the 1.5× target dimensions (255×144) using `sips`: `sips -z 144 255 /tmp/<name>.png --out static/img/logos/black/<name>.png`. This gives a crisper-on-retina raster at the design's intended display proportion, even if the resampled pixels don't add new detail.
|
||||
5. Save as `.png` (not `.svg` — partner logos in the grid are raster). Filename is kebab-case of the layer name with the `Logo_` prefix stripped (e.g. `Logo_DBS` → `db-schenker.png` — use the visually-recognized brand name, not the literal Figma slug if it's an abbreviation).
|
||||
6. **Surface logo-files annotations.** Designers often annotate one of the logo frames (e.g. `Logo_Circle`) with `data-assets-annotations` text like `"Need logo files"` plus a Google Drive link. When present, add a `MISSING_ASSETS` entry: `{ note: "Partner logos are placeholder reproductions — final brand-approved files pending from <Drive URL>", affects: [list of logo paths] }`. The exports are still useful as stand-ins until the user swaps in real assets.
|
||||
5. **Do NOT use `get_screenshot` on the section frame** to capture media — that re-renders the whole section (chrome, text, padding) and the resulting image cannot be used as a clean media src. The right target for media is the **named media frame inside the section** (e.g. `HeroMedia`, `FeatureMedia`, the `CardImage` media child) per Step 3a above, not the section itself and not the leaf `<img>` (which would strip the background fill).
|
||||
6. **Fallback when export fails or returns nothing usable:** create a zero-byte placeholder file at the target path AND record `{ figmaNodeId, layerName, targetPath, reason }` in a `MISSING_ASSETS` list. Do not silently drop the asset.
|
||||
|
||||
After all attempted exports, run `ls -la` on each unique target folder so the user can see what landed.
|
||||
|
||||
### Step 8 — Write the implementation (Plan Mode)
|
||||
|
||||
Present a plan first showing:
|
||||
- All sections in order with their mapped component
|
||||
- The full content (text, links, variants) for each section
|
||||
- Any content removed per Figma annotations
|
||||
- **For each list/grid/carousel section, the canonical item count and source** — e.g. "Logo grid: 8 distinct partner logos from Assets frame (LG mockup showed 8× placeholder, ignored)" or "Carousel: 5 cards from Assets frame (LG mockup showed 3 + 2 duplicates, kept 5)". Make it explicit that the Assets frame won, so the user can spot if something was missed.
|
||||
- **The asset manifest**: for every image/icon, show `figma layer → source (assets-frame node / section-instance fallback) → target path → status (exported | reused | missing)`
|
||||
|
||||
After approval, write the full `.page.tsx`:
|
||||
1. Keep the existing `frontmatter` export (SEO metadata)
|
||||
2. Import all section components at the top
|
||||
3. **Import and use the translate hook inside the default-exported component:**
|
||||
```tsx
|
||||
import { useThemeHooks } from '@redocly/theme/core/hooks';
|
||||
// ...
|
||||
export default function MyPage() {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
return (/* ... */);
|
||||
}
|
||||
```
|
||||
4. **Wrap every user-facing string in `translate(...)`** — this includes every prop value that renders as visible text. No bare string literals in JSX text or text-bearing props. Apply to:
|
||||
- `headline`, `subtitle`, `title`, `description`, `heading`, `label`, `iconAlt`, `alt` props
|
||||
- Every entry's `title`, `description`, `label`, `heading` inside `cards`, `links`, `buttons` arrays
|
||||
- `children` strings (including `callsToAction[].children` on `StandardCardGroupSection`)
|
||||
- Inline-link anchor text inside `React.ReactNode` descriptions (e.g. `<a href="...">{translate('Read More')}</a>`)
|
||||
- **Do not** translate URLs (`href`, `src`), variant strings (`"green"`, `"left"`), or `embedUrl` values
|
||||
- Example pattern (mirrors the project's house style):
|
||||
```tsx
|
||||
<HeaderHeroPrimaryMedia
|
||||
headline={translate("XRP Ledger (XRPL) Documentation")}
|
||||
subtitle={translate("Explore XRPL documentation with our essential guide for developers and admins who want to start building and integrating with the XRP Ledger.")}
|
||||
media={{ type: 'image', src: require('../static/img/bds-2026/docs-hero-media.jpg'), alt: translate('XRPL Documentation') }}
|
||||
/>
|
||||
```
|
||||
5. Render sections in Figma order inside a wrapper `<div className="landing">`
|
||||
6. For every image/icon, reference it via `require('../static/img/...')` using the exact path from the manifest — even if the export failed (the placeholder file holds the path)
|
||||
7. Apply all link `href` values from Figma `data-annotations`
|
||||
8. Use `React.ReactNode` descriptions to embed inline links where headings link to pages
|
||||
|
||||
### Step 9 — Fix TypeScript errors
|
||||
|
||||
After writing, check IDE diagnostics and fix:
|
||||
- Default vs named export mismatches
|
||||
- Wrong video source type (`type: 'embed'` not `type: 'embed_url'`)
|
||||
- Missing required props
|
||||
- `callsToAction` tuple shape `[primary, secondary?]`
|
||||
|
||||
### Step 10 — Report
|
||||
|
||||
End the run by printing:
|
||||
- The list of exported assets and their final paths
|
||||
- The `MISSING_ASSETS` list (if any) so the user knows exactly which Figma nodes need manual export
|
||||
- The full target file path
|
||||
|
||||
---
|
||||
|
||||
## Rules
|
||||
|
||||
- **Never install Tailwind** — the project uses SCSS + Bootstrap
|
||||
- **Never create new components** — only use what exists in `shared/sections/` and `shared/components/`
|
||||
- **The Assets frame is the source of truth.** Before fetching any section context, find and read the sibling Assets/Exports/Library/Resources frame on the page canvas. Use it for: real partner logos (not LG mockup placeholders), full carousel/list item sets (not the truncated visible cards), master icon artwork, and per-item hrefs the mockup hides. If you can't find an Assets frame and the design references one (via annotations or repeated placeholders), surface that to the user before guessing.
|
||||
- **Carousel and logo-grid item counts come from the Assets frame.** If the LG mockup shows repeated identical cards/logos, treat them as overflow placeholders and use the Assets-frame count instead.
|
||||
- **Export from Assets-frame nodes, not section instances.** Section instances are usually thumbnails of the master artwork in the Assets frame.
|
||||
- **Always apply links** from Figma annotations to make lists navigable
|
||||
- **Respect Figma notes** — if a section is annotated for removal, skip it; if it's annotated to redirect content to the Assets frame ("see right side asset frame", "logos in assets", etc.), follow that pointer rather than using the visible mockup content
|
||||
- **Light mode only** — follow the light mode variant of any design that shows both
|
||||
- **Always use `require('../static/img/...')` for images** — every image/icon prop must point at a real path under `static/img/` following the folder routing in Step 6. Never pass `src=""` and never inline a remote URL.
|
||||
- **Kebab-case all new asset filenames.** Shared photographic media in `bds-2026/` must be prefixed with the page slug; icons and logos are not page-prefixed.
|
||||
- **Reuse existing assets** when the inventory in Step 2 already has a matching icon/logo — do not re-export duplicates with new names.
|
||||
- **Never delete or overwrite an existing asset** without explicit user confirmation. If an export would collide with an existing file, rename the new export with a `-2` suffix and flag it in the plan.
|
||||
- **Always use `translate(...)` for visible text.** Import `useThemeHooks` from `@redocly/theme/core/hooks`, call `const { useTranslate } = useThemeHooks(); const { translate } = useTranslate();` at the top of the component, and wrap every user-facing string in `translate(...)`. Never leave a bare string literal in a text-bearing prop or as JSX text. URLs, variant enums, and embed URLs are not text — leave those untranslated.
|
||||
10
.claude/settings.json
Normal file
10
.claude/settings.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"mcp__claude_ai_Figma__get_design_context",
|
||||
"mcp__claude_ai_Figma__get_metadata",
|
||||
"mcp__claude_ai_Figma__get_screenshot",
|
||||
"mcp__claude_ai_Figma__whoami"
|
||||
]
|
||||
}
|
||||
}
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -8,7 +8,9 @@ yarn-error.log
|
||||
*.iml
|
||||
.venv/
|
||||
_code-samples/*/js/package-lock.json
|
||||
*.css.map
|
||||
_code-samples/*/*/*[Ss]etup.json
|
||||
|
||||
# PHP
|
||||
composer.lock
|
||||
.cursor/
|
||||
@@ -2,6 +2,7 @@ navbar.about: Acerca de
|
||||
navbar.docs: Docs
|
||||
navbar.resources: Recursos
|
||||
navbar.community: Comunidad
|
||||
navbar.showcase: Escaparate
|
||||
footer.about: Acerca de
|
||||
footer.docs: Docs
|
||||
footer.resources: Recursos
|
||||
|
||||
@@ -16,6 +16,7 @@ navbar.about: 概要
|
||||
navbar.docs: ドキュメント
|
||||
navbar.resources: リソース
|
||||
navbar.community: コミュニティ
|
||||
navbar.showcase: ショーケース
|
||||
footer.about: 概要
|
||||
footer.docs: ドキュメント
|
||||
footer.resources: リソース
|
||||
|
||||
@@ -1,449 +1,152 @@
|
||||
import * as React from "react";
|
||||
import { useThemeConfig, useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
import { LanguagePicker } from "@redocly/theme/components/LanguagePicker/LanguagePicker";
|
||||
import { slugify } from "../../helpers";
|
||||
import { Link } from "@redocly/theme/components/Link/Link";
|
||||
import { ColorModeSwitcher } from "@redocly/theme/components/ColorModeSwitcher/ColorModeSwitcher";
|
||||
import { Search } from "@redocly/theme/components/Search/Search";
|
||||
import arrowUpRight from "../../../static/img/icons/arrow-up-right-custom.svg";
|
||||
import moment from "moment-timezone";
|
||||
import { useSearchDialog } from "@redocly/theme/core/hooks";
|
||||
import { SearchDialog } from "@redocly/theme/components/Search/SearchDialog";
|
||||
|
||||
// @ts-ignore
|
||||
// Import from modular components
|
||||
import { AlertBanner } from "./components/AlertBanner";
|
||||
import { NavLogo } from "./components/NavLogo";
|
||||
import { NavItems } from "./components/NavItems";
|
||||
import { NavControls, HamburgerButton } from "./controls";
|
||||
import { DevelopSubmenu, UseCasesSubmenu, CommunitySubmenu, NetworkSubmenu } from "./submenus";
|
||||
import { MobileMenu } from "./mobile-menu";
|
||||
import { alertBanner } from "./constants/navigation";
|
||||
|
||||
const alertBanner = {
|
||||
show: false,
|
||||
message: "APEX 2025",
|
||||
button: "REGISTER",
|
||||
link: "https://www.xrpledgerapex.com/?utm_source=xrplwebsite&utm_medium=direct&utm_campaign=xrpl-event-ho-xrplapex-glb-2025-q1_xrplwebsite_ari_arp_bf_rsvp&utm_content=cta_btn_english_pencilbanner"
|
||||
};
|
||||
// Re-export AlertBanner for backwards compatibility
|
||||
export { AlertBanner } from "./components/AlertBanner";
|
||||
|
||||
export function AlertBanner({ message, button, link, show }) {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
const bannerRef = React.useRef(null);
|
||||
const [displayDate, setDisplayDate] = React.useState("JUNE 10-12");
|
||||
|
||||
React.useEffect(() => {
|
||||
const calculateCountdown = () => {
|
||||
// Calculate days until June 11, 2025 8AM Singapore time
|
||||
// This will automatically adjust for the user's timezone
|
||||
const target = moment.tz('2025-06-11 08:00:00', 'Asia/Singapore');
|
||||
const now = moment();
|
||||
const daysUntil = target.diff(now, 'days');
|
||||
|
||||
// Show countdown if event is in the future, otherwise show the provided date
|
||||
let newDisplayDate = "JUNE 10-12";
|
||||
if (daysUntil > 0) {
|
||||
newDisplayDate = daysUntil === 1 ? 'IN 1 DAY' : `IN ${daysUntil} DAYS`;
|
||||
} else if (daysUntil === 0) {
|
||||
// Check if it's today
|
||||
const hoursUntil = target.diff(now, 'hours');
|
||||
newDisplayDate = hoursUntil > 0 ? 'TODAY' : "JUNE 10-12";
|
||||
}
|
||||
|
||||
setDisplayDate(newDisplayDate);
|
||||
};
|
||||
|
||||
// Calculate immediately
|
||||
calculateCountdown();
|
||||
|
||||
// Update every hour
|
||||
const interval = setInterval(calculateCountdown, 60 * 60 * 1000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
const banner = bannerRef.current;
|
||||
if (!banner) return;
|
||||
const handleMouseEnter = () => {
|
||||
banner.classList.add("has-hover");
|
||||
};
|
||||
// Attach the event listener
|
||||
banner.addEventListener("mouseenter", handleMouseEnter);
|
||||
// Clean up the event listener on unmount
|
||||
return () => {
|
||||
banner.removeEventListener("mouseenter", handleMouseEnter);
|
||||
};
|
||||
}, []);
|
||||
|
||||
if (show) {
|
||||
return (
|
||||
<a
|
||||
href={link}
|
||||
target="_blank"
|
||||
ref={bannerRef}
|
||||
className="top-banner fixed-top web-banner"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="Get Tickets for the APEX 2025 Event"
|
||||
>
|
||||
<div className="banner-event-details">
|
||||
<div className="event-info">{translate(message)}</div>
|
||||
<div className="event-date">{displayDate}</div>
|
||||
</div>
|
||||
<div className="banner-button">
|
||||
<div className="button-text">{translate(button)}</div>
|
||||
<img
|
||||
className="button-icon"
|
||||
src={arrowUpRight}
|
||||
alt="Get Tickets Icon"
|
||||
/>
|
||||
</div>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
// Props interface for Navbar (extensible for future use)
|
||||
interface NavbarProps {
|
||||
className?: string;
|
||||
}
|
||||
export function Navbar(props) {
|
||||
// const [isOpen, setIsOpen] = useMobileMenu(false);
|
||||
const themeConfig = useThemeConfig();
|
||||
const { useL10n } = useThemeHooks();
|
||||
const { changeLanguage } = useL10n();
|
||||
const menu = themeConfig.navbar?.items;
|
||||
const logo = themeConfig.logo;
|
||||
|
||||
const { href, altText, items } = props;
|
||||
const pathPrefix = "";
|
||||
/**
|
||||
* Main Navbar Component.
|
||||
* Renders the complete navigation bar including:
|
||||
* - Alert banner (when enabled)
|
||||
* - Logo
|
||||
* - Navigation items with desktop submenus
|
||||
* - Control buttons (search, theme toggle, language)
|
||||
* - Mobile menu
|
||||
*/
|
||||
export function Navbar(_props: NavbarProps = {}) {
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = React.useState(false);
|
||||
const [activeSubmenu, setActiveSubmenu] = React.useState<string | null>(null);
|
||||
const [closingSubmenu, setClosingSubmenu] = React.useState<string | null>(null);
|
||||
const submenuTimeoutRef = React.useRef<NodeJS.Timeout | null>(null);
|
||||
const closingTimeoutRef = React.useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
const navItems = menu.map((item, index) => {
|
||||
if (item.type === "group") {
|
||||
return (
|
||||
<NavDropdown
|
||||
key={index}
|
||||
label={item.label}
|
||||
labelTranslationKey={item.labelTranslationKey}
|
||||
items={item.items}
|
||||
pathPrefix={pathPrefix}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<NavItem key={index}>
|
||||
<Link to={item.link} className="nav-link">
|
||||
{item.label}
|
||||
</Link>
|
||||
</NavItem>
|
||||
);
|
||||
// Use Redocly's search dialog hook - shared across navbar and mobile menu
|
||||
const { isOpen: isSearchOpen, onOpen: onSearchOpen, onClose: onSearchClose } = useSearchDialog();
|
||||
|
||||
const handleHamburgerClick = () => {
|
||||
setMobileMenuOpen(true);
|
||||
};
|
||||
|
||||
const handleMobileMenuClose = () => {
|
||||
setMobileMenuOpen(false);
|
||||
};
|
||||
|
||||
const handleSubmenuMouseEnter = (itemLabel: string) => {
|
||||
// Clear any pending close/closing timeouts
|
||||
if (submenuTimeoutRef.current) {
|
||||
clearTimeout(submenuTimeoutRef.current);
|
||||
submenuTimeoutRef.current = null;
|
||||
}
|
||||
});
|
||||
if (closingTimeoutRef.current) {
|
||||
clearTimeout(closingTimeoutRef.current);
|
||||
closingTimeoutRef.current = null;
|
||||
}
|
||||
// Cancel closing state and activate the new submenu
|
||||
setClosingSubmenu(null);
|
||||
setActiveSubmenu(itemLabel);
|
||||
};
|
||||
|
||||
const handleSubmenuMouseLeave = () => {
|
||||
submenuTimeoutRef.current = setTimeout(() => {
|
||||
// Start closing animation
|
||||
const currentSubmenu = activeSubmenu;
|
||||
if (currentSubmenu) {
|
||||
setClosingSubmenu(currentSubmenu);
|
||||
setActiveSubmenu(null);
|
||||
|
||||
// After animation completes (300ms), clear closing state
|
||||
closingTimeoutRef.current = setTimeout(() => {
|
||||
setClosingSubmenu(null);
|
||||
}, 350); // Slightly longer than animation to ensure completion
|
||||
}
|
||||
}, 150);
|
||||
};
|
||||
|
||||
const handleSubmenuClose = () => {
|
||||
// Close submenu immediately (for keyboard navigation)
|
||||
if (activeSubmenu) {
|
||||
setClosingSubmenu(activeSubmenu);
|
||||
setActiveSubmenu(null);
|
||||
|
||||
// After animation completes, clear closing state
|
||||
closingTimeoutRef.current = setTimeout(() => {
|
||||
setClosingSubmenu(null);
|
||||
}, 350);
|
||||
}
|
||||
};
|
||||
|
||||
// Handle scroll lock when submenu is open or closing
|
||||
React.useEffect(() => {
|
||||
if (activeSubmenu || closingSubmenu) {
|
||||
document.body.classList.add('bds-submenu-open');
|
||||
} else {
|
||||
document.body.classList.remove('bds-submenu-open');
|
||||
}
|
||||
return () => {
|
||||
document.body.classList.remove('bds-submenu-open');
|
||||
};
|
||||
}, [activeSubmenu, closingSubmenu]);
|
||||
|
||||
React.useEffect(() => {
|
||||
// Turns out jQuery is necessary for firing events on Bootstrap v4
|
||||
// dropdowns. These events set classes so that the search bar and other
|
||||
// submenus collapse on mobile when you expand one submenu.
|
||||
const dds = $("#topnav-pages .dropdown");
|
||||
const top_main_nav = document.querySelector("#top-main-nav");
|
||||
dds.on("show.bs.dropdown", (evt) => {
|
||||
top_main_nav.classList.add("submenu-expanded");
|
||||
});
|
||||
dds.on("hidden.bs.dropdown", (evt) => {
|
||||
top_main_nav.classList.remove("submenu-expanded");
|
||||
});
|
||||
// Close navbar on .dropdown-item click
|
||||
const toggleNavbar = () => {
|
||||
const navbarToggler = document.querySelector(".navbar-toggler");
|
||||
const isNavbarCollapsed =
|
||||
navbarToggler.getAttribute("aria-expanded") === "true";
|
||||
if (isNavbarCollapsed) {
|
||||
navbarToggler?.click(); // Simulate click to toggle navbar
|
||||
return () => {
|
||||
if (submenuTimeoutRef.current) {
|
||||
clearTimeout(submenuTimeoutRef.current);
|
||||
}
|
||||
if (closingTimeoutRef.current) {
|
||||
clearTimeout(closingTimeoutRef.current);
|
||||
}
|
||||
};
|
||||
|
||||
const dropdownItems = document.querySelectorAll(".dropdown-item");
|
||||
dropdownItems.forEach((item) => {
|
||||
item.addEventListener("click", toggleNavbar);
|
||||
});
|
||||
|
||||
// Cleanup function to remove event listeners
|
||||
return () => {
|
||||
dropdownItems.forEach((item) => {
|
||||
item.removeEventListener("click", toggleNavbar);
|
||||
});
|
||||
};
|
||||
}, []);
|
||||
|
||||
const navbarClasses = [
|
||||
"bds-navbar",
|
||||
alertBanner.show ? "bds-navbar--with-banner" : ""
|
||||
].filter(Boolean).join(" ");
|
||||
|
||||
return (
|
||||
<>
|
||||
<AlertBanner {...alertBanner} />
|
||||
<NavWrapper belowAlertBanner={alertBanner.show}>
|
||||
<LogoBlock to={href} img={logo} alt={altText} />
|
||||
<NavControls>
|
||||
<MobileMenuIcon />
|
||||
</NavControls>
|
||||
<TopNavCollapsible>
|
||||
<NavItems>
|
||||
{navItems}
|
||||
<div id="topnav-search" className="nav-item search">
|
||||
<Search className="topnav-search" />
|
||||
</div>
|
||||
<div id="topnav-language" className="nav-item">
|
||||
<LanguagePicker
|
||||
onChangeLanguage={changeLanguage}
|
||||
onlyIcon
|
||||
alignment="end"
|
||||
/>
|
||||
</div>
|
||||
<div id="topnav-theme" className="nav-item">
|
||||
<ColorModeSwitcher />
|
||||
</div>
|
||||
</NavItems>
|
||||
</TopNavCollapsible>
|
||||
</NavWrapper>
|
||||
{/* Backdrop blur overlay when submenu is open or closing */}
|
||||
<div
|
||||
className={`bds-submenu-backdrop ${activeSubmenu || closingSubmenu ? 'bds-submenu-backdrop--active' : ''}`}
|
||||
onClick={() => setActiveSubmenu(null)}
|
||||
/>
|
||||
<header
|
||||
className={navbarClasses}
|
||||
onMouseLeave={handleSubmenuMouseLeave}
|
||||
>
|
||||
<div className="bds-navbar__content">
|
||||
<NavLogo />
|
||||
<NavItems activeSubmenu={activeSubmenu} onSubmenuEnter={handleSubmenuMouseEnter} onSubmenuClose={handleSubmenuClose} />
|
||||
<NavControls onSearch={onSearchOpen} />
|
||||
<HamburgerButton onClick={handleHamburgerClick} />
|
||||
</div>
|
||||
{/* Submenus positioned relative to navbar */}
|
||||
<div onMouseEnter={() => activeSubmenu && handleSubmenuMouseEnter(activeSubmenu)}>
|
||||
<DevelopSubmenu isActive={activeSubmenu === 'Develop'} isClosing={closingSubmenu === 'Develop'} onClose={handleSubmenuClose} />
|
||||
<UseCasesSubmenu isActive={activeSubmenu === 'Use Cases'} isClosing={closingSubmenu === 'Use Cases'} onClose={handleSubmenuClose} />
|
||||
<CommunitySubmenu isActive={activeSubmenu === 'Community'} isClosing={closingSubmenu === 'Community'} onClose={handleSubmenuClose} />
|
||||
<NetworkSubmenu isActive={activeSubmenu === 'Network'} isClosing={closingSubmenu === 'Network'} onClose={handleSubmenuClose} />
|
||||
</div>
|
||||
</header>
|
||||
<MobileMenu isOpen={mobileMenuOpen} onClose={handleMobileMenuClose} onSearch={onSearchOpen} />
|
||||
{/* Render SearchDialog when open - this is the actual search modal */}
|
||||
{isSearchOpen && <SearchDialog onClose={onSearchClose} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function TopNavCollapsible({ children }) {
|
||||
return (
|
||||
<div
|
||||
className="collapse navbar-collapse justify-content-between"
|
||||
id="top-main-nav"
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function NavDropdown(props) {
|
||||
const { label, items, pathPrefix, labelTranslationKey } = props;
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
const dropdownGroups = items.map((item, index) => {
|
||||
if (item.items) {
|
||||
const groupLinks = item.items.map((item2, index2) => {
|
||||
const cls2 = item2.external
|
||||
? "dropdown-item external-link"
|
||||
: "dropdown-item";
|
||||
let item2_href = item2.link;
|
||||
if (item2_href && !item2_href.match(/^https?:/)) {
|
||||
item2_href = pathPrefix + item2_href;
|
||||
}
|
||||
//conditional specific for brand kit
|
||||
if (item2.link === "/XRPL_Brand_Kit.zip") {
|
||||
return (
|
||||
<a target="_blank" key={index2} href="/XRPL_Brand_Kit.zip" className={cls2}>
|
||||
{translate(item2.labelTranslationKey, item2.label)}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Link key={index2} className={cls2} to={item2_href}>
|
||||
{translate(item2.labelTranslationKey, item2.label)}
|
||||
</Link>
|
||||
);
|
||||
});
|
||||
|
||||
const clnm = "navcol col-for-" + slugify(item.label);
|
||||
|
||||
return (
|
||||
<div key={index} className={clnm}>
|
||||
<h5 className="dropdown-item">
|
||||
{translate(item.labelTranslationKey, item.label)}
|
||||
</h5>
|
||||
{groupLinks}
|
||||
</div>
|
||||
);
|
||||
} else if (item.icon) {
|
||||
const hero_id = "dropdown-hero-for-" + slugify(label);
|
||||
const img_alt = item.label + " icon";
|
||||
|
||||
let hero_href = item.link;
|
||||
if (hero_href && !hero_href.match(/^https?:/)) {
|
||||
hero_href = pathPrefix + hero_href;
|
||||
}
|
||||
const splitlabel = item.label.split(" || ");
|
||||
let splittranslationkey = ["", ""];
|
||||
if (item.labelTranslationKey) {
|
||||
splittranslationkey = item.labelTranslationKey.split(" || ");
|
||||
}
|
||||
const newlabel = translate(splittranslationkey[0], splitlabel[0]);
|
||||
const description = translate(splittranslationkey[1], splitlabel[1]); // splitlabel[1] might be undefined, that's ok
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={index}
|
||||
className="dropdown-item dropdown-hero"
|
||||
id={hero_id}
|
||||
to={hero_href}
|
||||
>
|
||||
<img id={item.hero} alt={img_alt} src={item.icon} />
|
||||
<div className="dropdown-hero-text">
|
||||
<h4>{newlabel}</h4>
|
||||
<p>{description}</p>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
} else {
|
||||
const cls = item.external
|
||||
? "dropdown-item ungrouped external-link"
|
||||
: "dropdown-item ungrouped";
|
||||
let item_href = item.link;
|
||||
if (item_href && !item_href.match(/^https?:/)) {
|
||||
item_href = pathPrefix + item_href;
|
||||
}
|
||||
return (
|
||||
<Link key={index} className={cls} to={item_href}>
|
||||
{translate(item.labelTranslationKey, item.label)}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const toggler_id = "topnav_" + slugify(label);
|
||||
const dd_id = "topnav_dd_" + slugify(label);
|
||||
|
||||
return (
|
||||
<li className="nav-item dropdown">
|
||||
<a
|
||||
className="nav-link dropdown-toggle"
|
||||
href="#"
|
||||
id={toggler_id}
|
||||
role="button"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<span>{translate(labelTranslationKey, label)}</span>
|
||||
</a>
|
||||
<div className="dropdown-menu" aria-labelledby={toggler_id} id={dd_id}>
|
||||
{dropdownGroups}
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
export function NavWrapper(props) {
|
||||
return (
|
||||
<nav
|
||||
className="top-nav navbar navbar-expand-lg navbar-dark fixed-top"
|
||||
style={props.belowAlertBanner ? { marginTop: "52px" } : {}}
|
||||
>
|
||||
{props.children}
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
export function NavControls(props) {
|
||||
return (
|
||||
<button
|
||||
className="navbar-toggler collapsed"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#top-main-nav"
|
||||
aria-controls="navbarHolder"
|
||||
aria-expanded="false"
|
||||
aria-label="Toggle navigation"
|
||||
>
|
||||
{props.children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export function MobileMenuIcon() {
|
||||
return (
|
||||
<span className="navbar-toggler-icon">
|
||||
<div></div>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
export function GetStartedButton() {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
return (
|
||||
<Link
|
||||
className="btn btn-primary"
|
||||
to={"/docs/tutorials"}
|
||||
style={{ height: "38px", paddingTop: "11px" }}
|
||||
>
|
||||
{translate("Get Started")}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
export function NavItems(props) {
|
||||
return (
|
||||
<ul className="nav navbar-nav" id="topnav-pages">
|
||||
{props.children}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
export function NavItem(props) {
|
||||
return <li className="nav-item">{props.children}</li>;
|
||||
}
|
||||
|
||||
export function LogoBlock(props) {
|
||||
const { to, img, altText } = props;
|
||||
return (
|
||||
<Link className="navbar-brand" to="/">
|
||||
<img className="logo" alt={"XRP LEDGER"} height="40" src="data:," />
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
export class ThemeToggle extends React.Component {
|
||||
auto_update_theme() {
|
||||
const upc = window.localStorage.getItem("user-prefers-color");
|
||||
let theme = "dark"; // Default to dark theme
|
||||
if (!upc) {
|
||||
// User hasn't saved a preference specifically for this site; check
|
||||
// the browser-level preferences.
|
||||
if (
|
||||
window.matchMedia &&
|
||||
window.matchMedia("(prefers-color-scheme: light)").matches
|
||||
) {
|
||||
theme = "light";
|
||||
}
|
||||
} else {
|
||||
// Follow user's saved setting.
|
||||
theme = upc == "light" ? "light" : "dark";
|
||||
}
|
||||
const disable_theme = theme == "dark" ? "light" : "dark";
|
||||
document.documentElement.classList.add(theme);
|
||||
document.documentElement.classList.remove(disable_theme);
|
||||
}
|
||||
|
||||
user_choose_theme() {
|
||||
const new_theme = document.documentElement.classList.contains("dark")
|
||||
? "light"
|
||||
: "dark";
|
||||
window.localStorage.setItem("user-prefers-color", new_theme);
|
||||
document.body.style.transition = "background-color .2s ease";
|
||||
const disable_theme = new_theme == "dark" ? "light" : "dark";
|
||||
document.documentElement.classList.add(new_theme);
|
||||
document.documentElement.classList.remove(disable_theme);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="nav-item" id="topnav-theme">
|
||||
<form className="form-inline">
|
||||
<div
|
||||
className="custom-control custom-theme-toggle form-inline-item"
|
||||
title=""
|
||||
data-toggle="tooltip"
|
||||
data-placement="left"
|
||||
data-original-title="Toggle Dark Mode"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
className="custom-control-input"
|
||||
id="css-toggle-btn"
|
||||
onClick={this.user_choose_theme}
|
||||
/>
|
||||
<label className="custom-control-label" htmlFor="css-toggle-btn">
|
||||
<span className="d-lg-none">Light/Dark Theme</span>
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.auto_update_theme();
|
||||
}
|
||||
}
|
||||
|
||||
82
@theme/components/Navbar/components/AlertBanner.tsx
Normal file
82
@theme/components/Navbar/components/AlertBanner.tsx
Normal file
@@ -0,0 +1,82 @@
|
||||
import * as React from "react";
|
||||
import { useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
import moment from "moment-timezone";
|
||||
import { arrowUpRight } from "../constants/icons";
|
||||
|
||||
interface AlertBannerProps {
|
||||
message: string;
|
||||
button: string;
|
||||
link: string;
|
||||
show: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alert Banner Component.
|
||||
* Displays a promotional banner at the top of the page.
|
||||
*/
|
||||
export function AlertBanner({ message, button, link, show }: AlertBannerProps) {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
const bannerRef = React.useRef<HTMLAnchorElement>(null);
|
||||
// Use null initial state to avoid hydration mismatch - server and client both render null initially
|
||||
const [displayDate, setDisplayDate] = React.useState<string | null>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
const calculateCountdown = () => {
|
||||
const target = moment.tz('2025-06-11 08:00:00', 'Asia/Singapore');
|
||||
const now = moment();
|
||||
const daysUntil = target.diff(now, 'days');
|
||||
|
||||
let newDisplayDate = "JUNE 10-12";
|
||||
if (daysUntil > 0) {
|
||||
newDisplayDate = daysUntil === 1 ? 'IN 1 DAY' : `IN ${daysUntil} DAYS`;
|
||||
} else if (daysUntil === 0) {
|
||||
const hoursUntil = target.diff(now, 'hours');
|
||||
newDisplayDate = hoursUntil > 0 ? 'TODAY' : "JUNE 10-12";
|
||||
}
|
||||
|
||||
setDisplayDate(newDisplayDate);
|
||||
};
|
||||
|
||||
calculateCountdown();
|
||||
const interval = setInterval(calculateCountdown, 60 * 60 * 1000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
const banner = bannerRef.current;
|
||||
if (!banner) return;
|
||||
|
||||
const handleMouseEnter = () => {
|
||||
banner.classList.add("has-hover");
|
||||
};
|
||||
|
||||
banner.addEventListener("mouseenter", handleMouseEnter);
|
||||
return () => {
|
||||
banner.removeEventListener("mouseenter", handleMouseEnter);
|
||||
};
|
||||
}, []);
|
||||
|
||||
if (!show) return null;
|
||||
|
||||
return (
|
||||
<a
|
||||
href={link}
|
||||
target="_blank"
|
||||
ref={bannerRef}
|
||||
className="top-banner fixed-top web-banner"
|
||||
rel="noopener noreferrer"
|
||||
aria-label={translate("Get Tickets for the APEX 2025 Event")}
|
||||
>
|
||||
<div className="banner-event-details">
|
||||
<div className="event-info">{translate(message)}</div>
|
||||
<div className="event-date">{displayDate ?? translate("JUNE 10-12")}</div>
|
||||
</div>
|
||||
<div className="banner-button">
|
||||
<div className="button-text">{translate(button)}</div>
|
||||
<img className="button-icon" src={arrowUpRight} alt="" />
|
||||
</div>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
114
@theme/components/Navbar/components/NavItems.tsx
Normal file
114
@theme/components/Navbar/components/NavItems.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import * as React from "react";
|
||||
import { useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
import { BdsLink } from "../../../../shared/components/Link/Link";
|
||||
import { navItems } from "../constants/navigation";
|
||||
|
||||
interface NavItemsProps {
|
||||
activeSubmenu: string | null;
|
||||
onSubmenuEnter: (itemLabel: string) => void;
|
||||
onSubmenuClose?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Nav Items Component.
|
||||
* Centered navigation links with submenu support.
|
||||
* ARIA compliant with full keyboard navigation support.
|
||||
*/
|
||||
export function NavItems({ activeSubmenu, onSubmenuEnter, onSubmenuClose }: NavItemsProps) {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
const [activeItem, setActiveItem] = React.useState<string | null>(null);
|
||||
|
||||
const handleMouseEnter = (itemLabel: string, hasSubmenu: boolean) => {
|
||||
setActiveItem(itemLabel);
|
||||
if (hasSubmenu) {
|
||||
onSubmenuEnter(itemLabel);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseLeave = (hasSubmenu: boolean) => {
|
||||
if (!hasSubmenu) {
|
||||
setActiveItem(null);
|
||||
}
|
||||
// Don't close submenu on leave - let the parent Navbar handle that
|
||||
};
|
||||
|
||||
const handleKeyDown = (event: React.KeyboardEvent, itemLabel: string) => {
|
||||
switch (event.key) {
|
||||
case 'Enter':
|
||||
case ' ':
|
||||
event.preventDefault();
|
||||
// Toggle submenu on Enter/Space
|
||||
if (activeSubmenu === itemLabel) {
|
||||
onSubmenuClose?.();
|
||||
} else {
|
||||
onSubmenuEnter(itemLabel);
|
||||
}
|
||||
break;
|
||||
case 'Escape':
|
||||
event.preventDefault();
|
||||
onSubmenuClose?.();
|
||||
break;
|
||||
case 'Tab':
|
||||
// If submenu is open and Tab is pressed (without Shift), move focus into submenu
|
||||
if (activeSubmenu === itemLabel && !event.shiftKey) {
|
||||
event.preventDefault();
|
||||
// Focus first focusable element in submenu
|
||||
const submenu = document.querySelector('.bds-submenu--active');
|
||||
const firstFocusable = submenu?.querySelector<HTMLElement>('a, button');
|
||||
firstFocusable?.focus();
|
||||
}
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
// If submenu is open, move focus into submenu
|
||||
if (activeSubmenu === itemLabel) {
|
||||
event.preventDefault();
|
||||
// Focus first focusable element in submenu
|
||||
const submenu = document.querySelector('.bds-submenu--active');
|
||||
const firstFocusable = submenu?.querySelector<HTMLElement>('a, button');
|
||||
firstFocusable?.focus();
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// Sync activeItem with activeSubmenu state
|
||||
React.useEffect(() => {
|
||||
if (!activeSubmenu) {
|
||||
setActiveItem(null);
|
||||
}
|
||||
}, [activeSubmenu]);
|
||||
|
||||
return (
|
||||
<nav className="bds-navbar__items" aria-label={translate("Main navigation")}>
|
||||
{navItems.map((item) => (
|
||||
item.hasSubmenu ? (
|
||||
<button
|
||||
key={item.label}
|
||||
type="button"
|
||||
className={`bds-navbar__item ${activeItem === item.label || activeSubmenu === item.label ? 'bds-navbar__item--active' : ''}`}
|
||||
onMouseEnter={() => handleMouseEnter(item.label, true)}
|
||||
onMouseLeave={() => handleMouseLeave(true)}
|
||||
onKeyDown={(e) => handleKeyDown(e, item.label)}
|
||||
aria-expanded={activeSubmenu === item.label}
|
||||
aria-haspopup="menu"
|
||||
>
|
||||
{translate(item.labelTranslationKey, item.label)}
|
||||
</button>
|
||||
) : (
|
||||
<BdsLink
|
||||
key={item.label}
|
||||
href={item.href}
|
||||
className={`bds-navbar__item ${activeItem === item.label ? 'bds-navbar__item--active' : ''}`}
|
||||
onMouseEnter={() => handleMouseEnter(item.label, false)}
|
||||
onMouseLeave={() => handleMouseLeave(false)}
|
||||
variant="inline"
|
||||
>
|
||||
{translate(item.labelTranslationKey, item.label)}
|
||||
</BdsLink>
|
||||
)
|
||||
))}
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
34
@theme/components/Navbar/components/NavLogo.tsx
Normal file
34
@theme/components/Navbar/components/NavLogo.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import { useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
import { BdsLink } from "../../../../shared/components/Link/Link";
|
||||
import { xrpSymbolBlack, xrpLogotypeBlack, xrpLedgerNav } from "../constants/icons";
|
||||
|
||||
/**
|
||||
* Nav Logo Component.
|
||||
* Shows symbol on desktop/mobile, full logotype on tablet.
|
||||
* On desktop hover, the "XRP LEDGER" text animates out to the right.
|
||||
*/
|
||||
export function NavLogo() {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
return (
|
||||
<BdsLink href="/" className="bds-navbar__logo" aria-label={translate("XRP Ledger Home")} variant="inline">
|
||||
<img
|
||||
src={xrpSymbolBlack}
|
||||
alt={translate("XRP Ledger")}
|
||||
className="bds-navbar__logo-symbol"
|
||||
/>
|
||||
<img
|
||||
src={xrpLedgerNav}
|
||||
alt=""
|
||||
className="bds-navbar__logo-text"
|
||||
/>
|
||||
<img
|
||||
src={xrpLogotypeBlack}
|
||||
alt={translate("XRP Ledger")}
|
||||
className="bds-navbar__logo-full"
|
||||
/>
|
||||
</BdsLink>
|
||||
);
|
||||
}
|
||||
|
||||
5
@theme/components/Navbar/components/index.ts
Normal file
5
@theme/components/Navbar/components/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
// Re-export navbar components
|
||||
export { AlertBanner } from './AlertBanner';
|
||||
export { NavLogo } from './NavLogo';
|
||||
export { NavItems } from './NavItems';
|
||||
|
||||
71
@theme/components/Navbar/constants/icons.ts
Normal file
71
@theme/components/Navbar/constants/icons.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
// Navbar icon imports
|
||||
|
||||
// Main navbar icons
|
||||
export { default as xrpSymbolBlack } from "../../../../static/img/navbar/xrp-symbol-black.svg";
|
||||
export { default as xrpLogotypeBlack } from "../../../../static/img/navbar/xrp-logotype-black.svg";
|
||||
export { default as xrpLedgerNav } from "../../../../static/img/navbar/xrp-ledger-nav.svg";
|
||||
export { default as searchIcon } from "../../../../static/img/navbar/search-icon.svg";
|
||||
export { default as modeToggleIcon } from "../../../../static/img/navbar/mode-toggle.svg";
|
||||
export { default as globeIcon } from "../../../../static/img/navbar/globe-icon.svg";
|
||||
export { default as chevronDown } from "../../../../static/img/navbar/chevron-down.svg";
|
||||
export { default as hamburgerIcon } from "../../../../static/img/navbar/hamburger-icon.svg";
|
||||
export { default as arrowUpRight } from "../../../../static/img/icons/arrow-up-right-custom.svg";
|
||||
|
||||
// Network submenu pattern images (used for both light and dark mode)
|
||||
export { default as resourcesIconPattern } from "../../../../static/img/navbar/resources-icon.svg";
|
||||
export { default as insightsIconPattern } from "../../../../static/img/navbar/insights-icon.svg";
|
||||
|
||||
// Submenu icons - imported once, exported individually and used in navIcons mapping
|
||||
import devHomeIcon from "../../../../static/img/navbar/dev_home.svg";
|
||||
import learnIcon from "../../../../static/img/navbar/learn.svg";
|
||||
import codeSamplesIcon from "../../../../static/img/navbar/code_samples.svg";
|
||||
import docsIcon from "../../../../static/img/navbar/docs.svg";
|
||||
import clientLibIcon from "../../../../static/img/navbar/client_lib.svg";
|
||||
import paymentsIcon from "../../../../static/img/navbar/payments.svg";
|
||||
import tokenizationIcon from "../../../../static/img/navbar/tokenization.svg";
|
||||
import creditIcon from "../../../../static/img/navbar/credit.svg";
|
||||
import tradingIcon from "../../../../static/img/navbar/trading.svg";
|
||||
import communityIcon from "../../../../static/img/navbar/community.svg";
|
||||
import fundingIcon from "../../../../static/img/navbar/funding.svg";
|
||||
import contributeIcon from "../../../../static/img/navbar/contribute.svg";
|
||||
import ecosystemIcon from "../../../../static/img/navbar/ecosystem.svg";
|
||||
import insightsIcon from "../../../../static/img/navbar/insights.svg";
|
||||
import resourcesIcon from "../../../../static/img/navbar/resources.svg";
|
||||
|
||||
// Re-export submenu icons for direct imports
|
||||
export default {
|
||||
devHomeIcon,
|
||||
learnIcon,
|
||||
codeSamplesIcon,
|
||||
docsIcon,
|
||||
clientLibIcon,
|
||||
paymentsIcon,
|
||||
tokenizationIcon,
|
||||
creditIcon,
|
||||
tradingIcon,
|
||||
communityIcon,
|
||||
fundingIcon,
|
||||
contributeIcon,
|
||||
ecosystemIcon,
|
||||
insightsIcon,
|
||||
resourcesIcon,
|
||||
};
|
||||
|
||||
// Dynamic icon lookup for navbar submenus
|
||||
export const navIcons: Record<string, string> = {
|
||||
dev_home: devHomeIcon,
|
||||
learn: learnIcon,
|
||||
code_samples: codeSamplesIcon,
|
||||
docs: docsIcon,
|
||||
client_lib: clientLibIcon,
|
||||
payments: paymentsIcon,
|
||||
tokenization: tokenizationIcon,
|
||||
credit: creditIcon,
|
||||
trading: tradingIcon,
|
||||
community: communityIcon,
|
||||
contribute: contributeIcon,
|
||||
insights: insightsIcon,
|
||||
resources: resourcesIcon,
|
||||
ecosystem: ecosystemIcon,
|
||||
funding: fundingIcon,
|
||||
};
|
||||
4
@theme/components/Navbar/constants/index.ts
Normal file
4
@theme/components/Navbar/constants/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
// Re-export all constants
|
||||
export * from './icons';
|
||||
export * from './navigation';
|
||||
|
||||
161
@theme/components/Navbar/constants/navigation.ts
Normal file
161
@theme/components/Navbar/constants/navigation.ts
Normal file
@@ -0,0 +1,161 @@
|
||||
import type { NavItem, SubmenuItemBase, SubmenuItemWithChildren, SubmenuItem, NetworkSubmenuSection } from '../types';
|
||||
|
||||
// Alert Banner Configuration
|
||||
export const alertBanner = {
|
||||
show: false,
|
||||
message: "APEX 2025",
|
||||
button: "REGISTER",
|
||||
link: "https://www.xrpledgerapex.com/?utm_source=xrplwebsite&utm_medium=direct&utm_campaign=xrpl-event-ho-xrplapex-glb-2025-q1_xrplwebsite_ari_arp_bf_rsvp&utm_content=cta_btn_english_pencilbanner"
|
||||
};
|
||||
|
||||
// Main navigation items
|
||||
export const navItems: NavItem[] = [
|
||||
{ label: "Develop", labelTranslationKey: "navbar.develop", href: "/develop", hasSubmenu: true },
|
||||
{ label: "Use Cases", labelTranslationKey: "navbar.usecases", href: "/use-cases", hasSubmenu: true },
|
||||
{ label: "Community", labelTranslationKey: "navbar.community", href: "/community", hasSubmenu: true },
|
||||
{ label: "Network", labelTranslationKey: "navbar.network", href: "/resources", hasSubmenu: true },
|
||||
{ label: "Showcase (Temporary)", labelTranslationKey: "navbar.showcase", href: "/showcase", hasSubmenu: false },
|
||||
];
|
||||
|
||||
// Develop submenu data structure
|
||||
export const developSubmenuData: {
|
||||
left: SubmenuItemBase[];
|
||||
right: SubmenuItemWithChildren[];
|
||||
} = {
|
||||
left: [
|
||||
{ label: "Developer's Home", href: "/develop", icon: "dev_home" },
|
||||
{ label: "Learn", href: "https://learn.xrpl.org", icon: "learn" },
|
||||
{ label: "Code Samples", href: "/resources/code-samples", icon: "code_samples" },
|
||||
],
|
||||
right: [
|
||||
{
|
||||
label: "Docs",
|
||||
href: "/docs",
|
||||
icon: "docs",
|
||||
children: [
|
||||
{ label: "API Reference", href: "/docs/references" },
|
||||
{ label: "Tutorials", href: "/docs/tutorials" },
|
||||
{ label: "Concepts", href: "/docs/concepts" },
|
||||
{ label: "Infrastructure", href: "/docs/infrastructure" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Client Libraries",
|
||||
href: "/docs/tutorials",
|
||||
icon: "client_lib",
|
||||
children: [
|
||||
{ label: "JavaScript", href: "/docs/tutorials/get-started/get-started-javascript" },
|
||||
{ label: "Python", href: "/docs/tutorials/get-started/get-started-python" },
|
||||
{ label: "PHP", href: "/docs/tutorials/get-started/get-started-php" },
|
||||
{ label: "Go", href: "/docs/tutorials/get-started/get-started-go" },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Use Cases submenu data structure
|
||||
export const useCasesSubmenuData: {
|
||||
left: SubmenuItemWithChildren[];
|
||||
right: SubmenuItemWithChildren[];
|
||||
} = {
|
||||
left: [
|
||||
{
|
||||
label: "Payments",
|
||||
href: "/docs/use-cases/payments",
|
||||
icon: "payments",
|
||||
children: [
|
||||
{ label: "Direct XRP Payments", href: "/docs/use-cases/payments/direct-xrp-payments" },
|
||||
{ label: "Cross-currency Payments", href: "/docs/use-cases/payments/cross-currency-payments" },
|
||||
{ label: "Escrow", href: "/docs/use-cases/payments/escrow" },
|
||||
{ label: "Checks", href: "/docs/use-cases/payments/checks" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Tokenization",
|
||||
href: "/docs/use-cases/tokenization",
|
||||
icon: "tokenization",
|
||||
children: [
|
||||
{ label: "Stablecoin", href: "/docs/use-cases/tokenization/stablecoin" },
|
||||
{ label: "NFT", href: "/docs/use-cases/tokenization/nft" },
|
||||
],
|
||||
},
|
||||
],
|
||||
right: [
|
||||
{
|
||||
label: "Credit",
|
||||
href: "/docs/use-cases/credit",
|
||||
icon: "credit",
|
||||
children: [
|
||||
{ label: "Lending", href: "/docs/use-cases/credit/lending" },
|
||||
{ label: "Collateralization", href: "/docs/use-cases/credit/collateralization" },
|
||||
{ label: "Sustainability", href: "/docs/use-cases/credit/sustainability" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Trading",
|
||||
href: "/docs/use-cases/trading",
|
||||
icon: "trading",
|
||||
children: [
|
||||
{ label: "DEX", href: "/docs/use-cases/trading/dex" },
|
||||
{ label: "Permissioned Trading", href: "/docs/use-cases/trading/permissioned-trading" },
|
||||
{ label: "AMM", href: "/docs/use-cases/trading/amm" },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Community submenu data structure
|
||||
export const communitySubmenuData: {
|
||||
left: SubmenuItem[];
|
||||
right: SubmenuItem[];
|
||||
} = {
|
||||
left: [
|
||||
{
|
||||
label: "Community",
|
||||
href: "/community",
|
||||
icon: "community",
|
||||
children: [
|
||||
{ label: "Events", href: "/community/events" },
|
||||
{ label: "Blog", href: "/blog" },
|
||||
],
|
||||
},
|
||||
{ label: "Funding", href: "/community/developer-funding", icon: "funding" },
|
||||
],
|
||||
right: [
|
||||
{
|
||||
label: "Contribute",
|
||||
href: "/community/contribute",
|
||||
icon: "contribute",
|
||||
children: [
|
||||
{ label: "Bug Bounty", href: "/blog/2020/rippled-1.5.0#bug-bounties-and-responsible-disclosures" },
|
||||
{ label: "Research", href: "https://xls.xrpl.org/" },
|
||||
],
|
||||
},
|
||||
{ label: "Ecosystem Map", href: "/about/uses", icon: "ecosystem" },
|
||||
],
|
||||
};
|
||||
|
||||
// Network submenu data
|
||||
export const networkSubmenuData: NetworkSubmenuSection[] = [
|
||||
{
|
||||
label: "Resources",
|
||||
href: "/resources",
|
||||
icon: "resources",
|
||||
children: [
|
||||
{ label: "About", href: "/about/history" },
|
||||
{ label: "XRPL Brand Kit", href: "/community/brand-kit" },
|
||||
],
|
||||
patternColor: 'lilac',
|
||||
},
|
||||
{
|
||||
label: "Insights",
|
||||
href: "/insights",
|
||||
icon: "insights",
|
||||
children: [
|
||||
{ label: "Explorer", href: "https://livenet.xrpl.org" },
|
||||
{ label: "Amendment Voting Status", href: "https://xrpl.org/resources/known-amendments" },
|
||||
],
|
||||
patternColor: 'green',
|
||||
},
|
||||
];
|
||||
|
||||
19
@theme/components/Navbar/controls/HamburgerButton.tsx
Normal file
19
@theme/components/Navbar/controls/HamburgerButton.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import { useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
import { IconButton } from "./IconButton";
|
||||
import { hamburgerIcon } from "../constants/icons";
|
||||
|
||||
interface HamburgerButtonProps {
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hamburger Menu Button Component.
|
||||
* Mobile-only button that opens the mobile menu.
|
||||
*/
|
||||
export function HamburgerButton({ onClick }: HamburgerButtonProps) {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
return <IconButton icon={hamburgerIcon} ariaLabel={translate("Open menu")} className="bds-navbar__hamburger" onClick={onClick} />;
|
||||
}
|
||||
|
||||
28
@theme/components/Navbar/controls/IconButton.tsx
Normal file
28
@theme/components/Navbar/controls/IconButton.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
interface IconButtonProps {
|
||||
/** The icon image source */
|
||||
icon: string;
|
||||
/** Accessible label for the button */
|
||||
ariaLabel: string;
|
||||
/** Optional click handler */
|
||||
onClick?: () => void;
|
||||
/** CSS class name for styling variants */
|
||||
className?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unified Icon Button component.
|
||||
* Used for search, mode toggle, hamburger menu, and other icon-only buttons.
|
||||
*/
|
||||
export function IconButton({ icon, ariaLabel, onClick, className = "bds-navbar__icon" }: IconButtonProps) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={className}
|
||||
aria-label={ariaLabel}
|
||||
onClick={onClick}
|
||||
>
|
||||
<img src={icon} alt="" />
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
85
@theme/components/Navbar/controls/LanguageDropdown.tsx
Normal file
85
@theme/components/Navbar/controls/LanguageDropdown.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
import * as React from "react";
|
||||
import { useLanguagePicker, useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
|
||||
interface LanguageDropdownProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Language Dropdown Component.
|
||||
* Displays available language options in a dropdown menu.
|
||||
* Based on Figma design: dark background with rounded corners.
|
||||
*/
|
||||
export function LanguageDropdown({ isOpen, onClose }: LanguageDropdownProps) {
|
||||
const { currentLocale, locales, setLocale } = useLanguagePicker();
|
||||
const { useL10n, useTranslate } = useThemeHooks();
|
||||
const { changeLanguage } = useL10n();
|
||||
const { translate } = useTranslate();
|
||||
const dropdownRef = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
// Handle clicking outside to close
|
||||
React.useEffect(() => {
|
||||
if (!isOpen) return;
|
||||
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
|
||||
// Check if click was on the language pill (parent trigger)
|
||||
const target = event.target as HTMLElement;
|
||||
if (!target.closest('.bds-navbar__lang-pill')) {
|
||||
onClose();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleEscape = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape') {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
document.addEventListener('keydown', handleEscape);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleClickOutside);
|
||||
document.removeEventListener('keydown', handleEscape);
|
||||
};
|
||||
}, [isOpen, onClose]);
|
||||
|
||||
if (!isOpen || locales.length < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleLanguageSelect = (localeCode: string) => {
|
||||
setLocale(localeCode);
|
||||
changeLanguage(localeCode);
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={dropdownRef}
|
||||
className="bds-lang-dropdown"
|
||||
role="menu"
|
||||
aria-label={translate("Language selection")}
|
||||
>
|
||||
{locales.map((locale) => {
|
||||
const isActive = locale.code === currentLocale?.code;
|
||||
return (
|
||||
<button
|
||||
key={locale.code}
|
||||
type="button"
|
||||
role="menuitem"
|
||||
className={`bds-lang-dropdown__item ${isActive ? 'bds-lang-dropdown__item--active' : ''}`}
|
||||
onClick={() => handleLanguageSelect(locale.code)}
|
||||
aria-current={isActive ? 'true' : undefined}
|
||||
>
|
||||
{locale.name || locale.code}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
53
@theme/components/Navbar/controls/LanguagePill.tsx
Normal file
53
@theme/components/Navbar/controls/LanguagePill.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import { useLanguagePicker, useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
import { globeIcon, chevronDown } from "../constants/icons";
|
||||
|
||||
interface LanguagePillProps {
|
||||
onClick?: () => void;
|
||||
isOpen?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get short display name for a locale code.
|
||||
* e.g., "en-US" -> "En", "ja" -> "日本語"
|
||||
*/
|
||||
function getLocaleShortName(code: string | undefined): string {
|
||||
if (!code) return "En";
|
||||
|
||||
// Map locale codes to short display names
|
||||
const shortNames: Record<string, string> = {
|
||||
"en-US": "En",
|
||||
"en": "En",
|
||||
"ja": "日本語",
|
||||
};
|
||||
|
||||
return shortNames[code] || code.substring(0, 2).toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Language Pill Button Component.
|
||||
* Shows current language and opens language selector.
|
||||
*/
|
||||
export function LanguagePill({ onClick, isOpen }: LanguagePillProps) {
|
||||
const { currentLocale } = useLanguagePicker();
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
const displayName = getLocaleShortName(currentLocale?.code);
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={`bds-navbar__lang-pill ${isOpen ? 'bds-navbar__lang-pill--open' : ''}`}
|
||||
aria-label={translate("Select language")}
|
||||
aria-expanded={isOpen}
|
||||
aria-haspopup="menu"
|
||||
onClick={onClick}
|
||||
>
|
||||
<img src={globeIcon} alt="" className="bds-navbar__lang-pill-icon" />
|
||||
<span className="bds-navbar__lang-pill-text">
|
||||
<span>{displayName}</span>
|
||||
<img src={chevronDown} alt="" className="bds-navbar__lang-pill-chevron" />
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
19
@theme/components/Navbar/controls/ModeToggleButton.tsx
Normal file
19
@theme/components/Navbar/controls/ModeToggleButton.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import { useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
import { IconButton } from "./IconButton";
|
||||
import { modeToggleIcon } from "../constants/icons";
|
||||
|
||||
interface ModeToggleButtonProps {
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mode Toggle Button Component.
|
||||
* Icon button that toggles between light and dark mode.
|
||||
*/
|
||||
export function ModeToggleButton({ onClick }: ModeToggleButtonProps) {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
return <IconButton icon={modeToggleIcon} ariaLabel={translate("Toggle color mode")} onClick={onClick} />;
|
||||
}
|
||||
|
||||
46
@theme/components/Navbar/controls/NavControls.tsx
Normal file
46
@theme/components/Navbar/controls/NavControls.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import * as React from "react";
|
||||
import { SearchButton } from "./SearchButton";
|
||||
import { ModeToggleButton } from "./ModeToggleButton";
|
||||
import { LanguagePill } from "./LanguagePill";
|
||||
import { LanguageDropdown } from "./LanguageDropdown";
|
||||
|
||||
interface NavControlsProps {
|
||||
onSearch?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Nav Controls Component.
|
||||
* Right side of the navbar containing search, mode toggle, and language selector.
|
||||
*/
|
||||
export function NavControls({ onSearch }: NavControlsProps) {
|
||||
const [isLanguageDropdownOpen, setIsLanguageDropdownOpen] = React.useState(false);
|
||||
|
||||
const handleModeToggle = () => {
|
||||
// Toggle between light and dark theme
|
||||
const newTheme = document.documentElement.classList.contains("dark") ? "light" : "dark";
|
||||
window.localStorage.setItem("user-prefers-color", newTheme);
|
||||
document.body.style.transition = "background-color .2s ease";
|
||||
document.documentElement.classList.remove("dark", "light");
|
||||
document.documentElement.classList.add(newTheme);
|
||||
};
|
||||
|
||||
const handleLanguageClick = () => {
|
||||
setIsLanguageDropdownOpen(!isLanguageDropdownOpen);
|
||||
};
|
||||
|
||||
const handleLanguageDropdownClose = () => {
|
||||
setIsLanguageDropdownOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bds-navbar__controls">
|
||||
<SearchButton onClick={onSearch} />
|
||||
<ModeToggleButton onClick={handleModeToggle} />
|
||||
<div className="bds-navbar__lang-wrapper">
|
||||
<LanguagePill onClick={handleLanguageClick} isOpen={isLanguageDropdownOpen} />
|
||||
<LanguageDropdown isOpen={isLanguageDropdownOpen} onClose={handleLanguageDropdownClose} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
19
@theme/components/Navbar/controls/SearchButton.tsx
Normal file
19
@theme/components/Navbar/controls/SearchButton.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import { useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
import { IconButton } from "./IconButton";
|
||||
import { searchIcon } from "../constants/icons";
|
||||
|
||||
interface SearchButtonProps {
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search Button Component.
|
||||
* Icon button that triggers the search modal.
|
||||
*/
|
||||
export function SearchButton({ onClick }: SearchButtonProps) {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
return <IconButton icon={searchIcon} ariaLabel={translate("Search")} onClick={onClick} />;
|
||||
}
|
||||
|
||||
11
@theme/components/Navbar/controls/index.ts
Normal file
11
@theme/components/Navbar/controls/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
// Unified base component
|
||||
export { IconButton } from './IconButton';
|
||||
|
||||
// Specific button implementations (use IconButton internally)
|
||||
export { NavControls } from './NavControls';
|
||||
export { SearchButton } from './SearchButton';
|
||||
export { ModeToggleButton } from './ModeToggleButton';
|
||||
export { LanguagePill } from './LanguagePill';
|
||||
export { LanguageDropdown } from './LanguageDropdown';
|
||||
export { HamburgerButton } from './HamburgerButton';
|
||||
|
||||
30
@theme/components/Navbar/icons/ChevronIcon.tsx
Normal file
30
@theme/components/Navbar/icons/ChevronIcon.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import * as React from "react";
|
||||
|
||||
interface ChevronIconProps {
|
||||
expanded: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Chevron Icon Component for Mobile Accordion
|
||||
*/
|
||||
export function ChevronIcon({ expanded }: ChevronIconProps) {
|
||||
return (
|
||||
<svg
|
||||
className={`bds-mobile-menu__chevron ${expanded ? 'bds-mobile-menu__chevron--expanded' : ''}`}
|
||||
width="13"
|
||||
height="8"
|
||||
viewBox="0 0 13 8"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M1 1L6.5 6.5L12 1"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
14
@theme/components/Navbar/icons/CloseIcon.tsx
Normal file
14
@theme/components/Navbar/icons/CloseIcon.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import * as React from "react";
|
||||
|
||||
/**
|
||||
* Close Icon Component for Mobile Menu
|
||||
*/
|
||||
export function CloseIcon() {
|
||||
return (
|
||||
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<line x1="7" y1="7" x2="21" y2="21" stroke="#141414" strokeWidth="2" strokeLinecap="round" />
|
||||
<line x1="21" y1="7" x2="7" y2="21" stroke="#141414" strokeWidth="2" strokeLinecap="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
56
@theme/components/Navbar/icons/SubmenuArrow.tsx
Normal file
56
@theme/components/Navbar/icons/SubmenuArrow.tsx
Normal file
@@ -0,0 +1,56 @@
|
||||
interface ArrowIconProps {
|
||||
className?: string;
|
||||
color?: string;
|
||||
/**
|
||||
* When true, the horizontal line has a class for CSS animation (parent links).
|
||||
* When false, the full arrow is shown without animation class (child links).
|
||||
*/
|
||||
animated?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unified Arrow Icon component.
|
||||
* - For parent links (animated=true): horizontal line animates away on hover
|
||||
* - For child links (animated=false): full static arrow
|
||||
*/
|
||||
export function ArrowIcon({ className, color = "currentColor", animated = true }: ArrowIconProps) {
|
||||
return (
|
||||
<svg
|
||||
className={className}
|
||||
width="15"
|
||||
height="14"
|
||||
viewBox="0 0 26 22"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-hidden="true"
|
||||
>
|
||||
{/* Chevron part */}
|
||||
<path
|
||||
d="M14.0019 1.00191L24.0015 11.0015L14.0019 21.001"
|
||||
stroke={color}
|
||||
strokeWidth="2"
|
||||
strokeMiterlimit="10"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
{/* Horizontal line */}
|
||||
<path
|
||||
d="M23.999 10.999H0"
|
||||
stroke={color}
|
||||
strokeWidth="2"
|
||||
strokeMiterlimit="10"
|
||||
strokeLinecap="round"
|
||||
className={animated ? "arrow-horizontal" : undefined}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
// Backwards-compatible aliases
|
||||
export const SubmenuArrow = (props: Omit<ArrowIconProps, 'animated'>) => (
|
||||
<ArrowIcon {...props} animated={true} />
|
||||
);
|
||||
|
||||
export const SubmenuChildArrow = (props: Omit<ArrowIconProps, 'animated'>) => (
|
||||
<ArrowIcon {...props} animated={false} />
|
||||
);
|
||||
|
||||
6
@theme/components/Navbar/icons/index.ts
Normal file
6
@theme/components/Navbar/icons/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
// Re-export all icon components
|
||||
// Unified arrow icon with backwards-compatible aliases
|
||||
export { ArrowIcon, SubmenuArrow, SubmenuChildArrow } from './SubmenuArrow';
|
||||
export { CloseIcon } from './CloseIcon';
|
||||
export { ChevronIcon } from './ChevronIcon';
|
||||
|
||||
13
@theme/components/Navbar/index.ts
Normal file
13
@theme/components/Navbar/index.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
// Main Navbar component
|
||||
export { Navbar } from './Navbar';
|
||||
|
||||
// Re-export types
|
||||
export * from './types';
|
||||
|
||||
// Re-export components for advanced usage
|
||||
export * from './components';
|
||||
export * from './controls';
|
||||
export * from './submenus';
|
||||
export * from './mobile-menu';
|
||||
export * from './icons';
|
||||
|
||||
215
@theme/components/Navbar/mobile-menu/MobileMenu.tsx
Normal file
215
@theme/components/Navbar/mobile-menu/MobileMenu.tsx
Normal file
@@ -0,0 +1,215 @@
|
||||
import * as React from "react";
|
||||
import { useThemeHooks, useLanguagePicker } from "@redocly/theme/core/hooks";
|
||||
import { BdsLink } from "../../../../shared/components/Link/Link";
|
||||
import { CloseIcon, ChevronIcon } from "../icons";
|
||||
import { xrpSymbolBlack, globeIcon, chevronDown, modeToggleIcon, searchIcon } from "../constants/icons";
|
||||
import { navItems } from "../constants/navigation";
|
||||
import { MobileMenuContent, type MobileMenuKey } from "./MobileMenuContent";
|
||||
|
||||
interface MobileMenuProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSearch?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mobile Menu Component.
|
||||
* Full-screen slide-out menu for mobile devices.
|
||||
*/
|
||||
export function MobileMenu({ isOpen, onClose, onSearch }: MobileMenuProps) {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
const [expandedItem, setExpandedItem] = React.useState<string | null>("Develop");
|
||||
|
||||
// Handle body scroll lock
|
||||
React.useEffect(() => {
|
||||
if (isOpen) {
|
||||
document.body.classList.add('bds-mobile-menu-open');
|
||||
} else {
|
||||
document.body.classList.remove('bds-mobile-menu-open');
|
||||
}
|
||||
return () => {
|
||||
document.body.classList.remove('bds-mobile-menu-open');
|
||||
};
|
||||
}, [isOpen]);
|
||||
|
||||
const toggleAccordion = (item: string) => {
|
||||
setExpandedItem(expandedItem === item ? null : item);
|
||||
};
|
||||
|
||||
const handleSearch = () => {
|
||||
if (onSearch) {
|
||||
onSearch();
|
||||
}
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleModeToggle = () => {
|
||||
const newTheme = document.documentElement.classList.contains("dark") ? "light" : "dark";
|
||||
window.localStorage.setItem("user-prefers-color", newTheme);
|
||||
document.body.style.transition = "background-color .2s ease";
|
||||
document.documentElement.classList.remove("dark", "light");
|
||||
document.documentElement.classList.add(newTheme);
|
||||
};
|
||||
|
||||
const renderAccordionContent = (label: string) => {
|
||||
// All nav items with submenus use the unified MobileMenuContent
|
||||
const validKeys: MobileMenuKey[] = ['Develop', 'Use Cases', 'Community', 'Network'];
|
||||
if (validKeys.includes(label as MobileMenuKey)) {
|
||||
return <MobileMenuContent menuKey={label as MobileMenuKey} />;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`bds-mobile-menu ${isOpen ? 'bds-mobile-menu--open' : ''}`}>
|
||||
{/* Header */}
|
||||
<div className="bds-mobile-menu__header">
|
||||
<BdsLink href="/" className="bds-navbar__logo" aria-label={translate("XRP Ledger Home")} onClick={onClose} variant="inline">
|
||||
<img src={xrpSymbolBlack} alt={translate("XRP Ledger")} className="bds-navbar__logo-symbol" style={{ width: 33, height: 28 }} />
|
||||
</BdsLink>
|
||||
<button
|
||||
type="button"
|
||||
className="bds-mobile-menu__close"
|
||||
aria-label={translate("Close menu")}
|
||||
onClick={onClose}
|
||||
>
|
||||
<CloseIcon />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="bds-mobile-menu__content">
|
||||
<div className="bds-mobile-menu__accordion">
|
||||
{navItems.map((item) => (
|
||||
<React.Fragment key={item.label}>
|
||||
<button
|
||||
type="button"
|
||||
className="bds-mobile-menu__accordion-header"
|
||||
onClick={() => item.hasSubmenu ? toggleAccordion(item.label) : null}
|
||||
aria-expanded={expandedItem === item.label}
|
||||
>
|
||||
{item.hasSubmenu ? (
|
||||
<>
|
||||
<span>{translate(item.labelTranslationKey, item.label)}</span>
|
||||
<ChevronIcon expanded={expandedItem === item.label} />
|
||||
</>
|
||||
) : (
|
||||
<BdsLink
|
||||
href={item.href}
|
||||
onClick={onClose}
|
||||
variant="inline"
|
||||
style={{
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
color: 'inherit',
|
||||
textDecoration: 'none'
|
||||
}}
|
||||
>
|
||||
<span>{translate(item.labelTranslationKey, item.label)}</span>
|
||||
<ChevronIcon expanded={false} />
|
||||
</BdsLink>
|
||||
)}
|
||||
</button>
|
||||
{item.hasSubmenu && (
|
||||
<div
|
||||
className={`bds-mobile-menu__accordion-content ${
|
||||
expandedItem === item.label ? 'bds-mobile-menu__accordion-content--expanded' : ''
|
||||
}`}
|
||||
>
|
||||
{renderAccordionContent(item.label)}
|
||||
</div>
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<MobileMenuFooter
|
||||
onModeToggle={handleModeToggle}
|
||||
onSearch={handleSearch}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface MobileMenuFooterProps {
|
||||
onModeToggle: () => void;
|
||||
onSearch: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get short display name for a locale code.
|
||||
*/
|
||||
function getLocaleShortName(code: string | undefined): string {
|
||||
if (!code) return "En";
|
||||
const shortNames: Record<string, string> = {
|
||||
"en-US": "En",
|
||||
"en": "En",
|
||||
"ja": "日本語",
|
||||
};
|
||||
return shortNames[code] || code.substring(0, 2).toUpperCase();
|
||||
}
|
||||
|
||||
function MobileMenuFooter({ onModeToggle, onSearch }: MobileMenuFooterProps) {
|
||||
const { currentLocale, locales, setLocale } = useLanguagePicker();
|
||||
const { useL10n, useTranslate } = useThemeHooks();
|
||||
const { changeLanguage } = useL10n();
|
||||
const { translate } = useTranslate();
|
||||
const [isLangOpen, setIsLangOpen] = React.useState(false);
|
||||
const displayName = getLocaleShortName(currentLocale?.code);
|
||||
|
||||
const handleLanguageSelect = (localeCode: string) => {
|
||||
setLocale(localeCode);
|
||||
changeLanguage(localeCode);
|
||||
setIsLangOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bds-mobile-menu__footer">
|
||||
<div className="bds-mobile-menu__lang-wrapper">
|
||||
<button
|
||||
type="button"
|
||||
className={`bds-mobile-menu__lang-pill ${isLangOpen ? 'bds-mobile-menu__lang-pill--open' : ''}`}
|
||||
aria-label={translate("Select language")}
|
||||
aria-expanded={isLangOpen}
|
||||
onClick={() => setIsLangOpen(!isLangOpen)}
|
||||
>
|
||||
<img src={globeIcon} alt="" className="bds-mobile-menu__lang-pill-icon" />
|
||||
<span className="bds-mobile-menu__lang-pill-text">
|
||||
<span>{displayName}</span>
|
||||
<img src={chevronDown} alt="" className="bds-mobile-menu__lang-pill-chevron" />
|
||||
</span>
|
||||
</button>
|
||||
{isLangOpen && locales.length >= 2 && (
|
||||
<div className="bds-lang-dropdown bds-lang-dropdown--mobile" role="menu">
|
||||
{locales.map((locale) => {
|
||||
const isActive = locale.code === currentLocale?.code;
|
||||
return (
|
||||
<button
|
||||
key={locale.code}
|
||||
type="button"
|
||||
role="menuitem"
|
||||
className={`bds-lang-dropdown__item ${isActive ? 'bds-lang-dropdown__item--active' : ''}`}
|
||||
onClick={() => handleLanguageSelect(locale.code)}
|
||||
>
|
||||
{locale.name || locale.code}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<button type="button" className="bds-mobile-menu__footer-icon" aria-label={translate("Toggle color mode")} onClick={onModeToggle}>
|
||||
<img src={modeToggleIcon} alt="" />
|
||||
</button>
|
||||
<button type="button" className="bds-mobile-menu__footer-icon" aria-label={translate("Search")} onClick={onSearch}>
|
||||
<img src={searchIcon} alt="" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
47
@theme/components/Navbar/mobile-menu/MobileMenuContent.tsx
Normal file
47
@theme/components/Navbar/mobile-menu/MobileMenuContent.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import { MobileMenuSection } from "./MobileMenuSection";
|
||||
import { developSubmenuData, useCasesSubmenuData, communitySubmenuData, networkSubmenuData } from "../constants/navigation";
|
||||
import type { SubmenuItem, SubmenuItemWithChildren, NetworkSubmenuSection } from "../types";
|
||||
|
||||
export type MobileMenuKey = 'Develop' | 'Use Cases' | 'Community' | 'Network';
|
||||
|
||||
interface MobileMenuContentProps {
|
||||
/** Which menu section to render */
|
||||
menuKey: MobileMenuKey;
|
||||
}
|
||||
|
||||
/** Get flattened menu items based on menu key */
|
||||
function getMenuItems(menuKey: MobileMenuKey): (SubmenuItem | SubmenuItemWithChildren | NetworkSubmenuSection)[] {
|
||||
switch (menuKey) {
|
||||
case 'Develop':
|
||||
return [...developSubmenuData.left, ...developSubmenuData.right];
|
||||
case 'Use Cases':
|
||||
return [...useCasesSubmenuData.left, ...useCasesSubmenuData.right];
|
||||
case 'Community':
|
||||
return [...communitySubmenuData.left, ...communitySubmenuData.right];
|
||||
case 'Network':
|
||||
return networkSubmenuData;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unified Mobile Menu Content component.
|
||||
* Renders accordion content for any menu section.
|
||||
*/
|
||||
export function MobileMenuContent({ menuKey }: MobileMenuContentProps) {
|
||||
const items = getMenuItems(menuKey);
|
||||
|
||||
return (
|
||||
<div className="bds-mobile-menu__tier-list">
|
||||
{items.map((item) => (
|
||||
<MobileMenuSection key={item.label} item={item} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Backwards-compatible named exports
|
||||
export const MobileMenuDevelopContent = () => <MobileMenuContent menuKey="Develop" />;
|
||||
export const MobileMenuUseCasesContent = () => <MobileMenuContent menuKey="Use Cases" />;
|
||||
export const MobileMenuCommunityContent = () => <MobileMenuContent menuKey="Community" />;
|
||||
export const MobileMenuNetworkContent = () => <MobileMenuContent menuKey="Network" />;
|
||||
|
||||
54
@theme/components/Navbar/mobile-menu/MobileMenuSection.tsx
Normal file
54
@theme/components/Navbar/mobile-menu/MobileMenuSection.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import * as React from "react";
|
||||
import { useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
import { SubmenuArrow, SubmenuChildArrow } from "../icons";
|
||||
import { navIcons } from "../constants/icons";
|
||||
import { hasChildren, type SubmenuItem } from "../types";
|
||||
|
||||
interface MobileMenuSectionProps {
|
||||
item: SubmenuItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reusable mobile menu section component.
|
||||
* Renders a parent link with icon, and optionally child links.
|
||||
*/
|
||||
export function MobileMenuSection({ item }: MobileMenuSectionProps) {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
const itemHasChildren = hasChildren(item);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<a href={item.href} className="bds-mobile-menu__tier1 bds-mobile-menu__parent-link">
|
||||
<span className="bds-mobile-menu__icon">
|
||||
<img src={navIcons[item.icon]} alt={translate(item.label)} />
|
||||
</span>
|
||||
<span className="bds-mobile-menu__link bds-mobile-menu__link--bold">
|
||||
{translate(item.label)}
|
||||
<span className="bds-mobile-menu__arrow">
|
||||
<SubmenuArrow />
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
{itemHasChildren && (
|
||||
<div className="bds-mobile-menu__tier2">
|
||||
{item.children.map((child) => (
|
||||
<a
|
||||
key={child.label}
|
||||
href={child.href}
|
||||
className="bds-mobile-menu__sublink"
|
||||
target={child.href.startsWith('http') ? '_blank' : undefined}
|
||||
rel={child.href.startsWith('http') ? 'noopener noreferrer' : undefined}
|
||||
>
|
||||
{translate(child.label)}
|
||||
<span className="bds-mobile-menu__sublink-arrow">
|
||||
<SubmenuChildArrow />
|
||||
</span>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
16
@theme/components/Navbar/mobile-menu/index.ts
Normal file
16
@theme/components/Navbar/mobile-menu/index.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
// Main mobile menu component
|
||||
export { MobileMenu } from './MobileMenu';
|
||||
|
||||
// Unified content component with backwards-compatible aliases
|
||||
export {
|
||||
MobileMenuContent,
|
||||
MobileMenuDevelopContent,
|
||||
MobileMenuUseCasesContent,
|
||||
MobileMenuCommunityContent,
|
||||
MobileMenuNetworkContent,
|
||||
type MobileMenuKey
|
||||
} from './MobileMenuContent';
|
||||
|
||||
// Reusable section component
|
||||
export { MobileMenuSection } from './MobileMenuSection';
|
||||
|
||||
16
@theme/components/Navbar/submenus/CommunitySubmenu.tsx
Normal file
16
@theme/components/Navbar/submenus/CommunitySubmenu.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Submenu } from "./Submenu";
|
||||
|
||||
interface CommunitySubmenuProps {
|
||||
isActive: boolean;
|
||||
isClosing: boolean;
|
||||
onClose?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Desktop Community Submenu Component.
|
||||
* Wrapper for unified Submenu component with 'community' variant.
|
||||
*/
|
||||
export function CommunitySubmenu({ isActive, isClosing, onClose }: CommunitySubmenuProps) {
|
||||
return <Submenu variant="community" isActive={isActive} isClosing={isClosing} onClose={onClose} />;
|
||||
}
|
||||
|
||||
16
@theme/components/Navbar/submenus/DevelopSubmenu.tsx
Normal file
16
@theme/components/Navbar/submenus/DevelopSubmenu.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Submenu } from "./Submenu";
|
||||
|
||||
interface DevelopSubmenuProps {
|
||||
isActive: boolean;
|
||||
isClosing: boolean;
|
||||
onClose?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Desktop Develop Submenu Component.
|
||||
* Wrapper for unified Submenu component with 'develop' variant.
|
||||
*/
|
||||
export function DevelopSubmenu({ isActive, isClosing, onClose }: DevelopSubmenuProps) {
|
||||
return <Submenu variant="develop" isActive={isActive} isClosing={isClosing} onClose={onClose} />;
|
||||
}
|
||||
|
||||
16
@theme/components/Navbar/submenus/NetworkSubmenu.tsx
Normal file
16
@theme/components/Navbar/submenus/NetworkSubmenu.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Submenu } from "./Submenu";
|
||||
|
||||
interface NetworkSubmenuProps {
|
||||
isActive: boolean;
|
||||
isClosing: boolean;
|
||||
onClose?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Desktop Network Submenu Component.
|
||||
* Wrapper for unified Submenu component with 'network' variant.
|
||||
*/
|
||||
export function NetworkSubmenu({ isActive, isClosing, onClose }: NetworkSubmenuProps) {
|
||||
return <Submenu variant="network" isActive={isActive} isClosing={isClosing} onClose={onClose} />;
|
||||
}
|
||||
|
||||
277
@theme/components/Navbar/submenus/Submenu.tsx
Normal file
277
@theme/components/Navbar/submenus/Submenu.tsx
Normal file
@@ -0,0 +1,277 @@
|
||||
import * as React from "react";
|
||||
import { useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
import { SubmenuSection } from "./SubmenuSection";
|
||||
import { ArrowIcon } from "../icons";
|
||||
import { navIcons, resourcesIconPattern, insightsIconPattern } from "../constants/icons";
|
||||
import { developSubmenuData, useCasesSubmenuData, communitySubmenuData, networkSubmenuData } from "../constants/navigation";
|
||||
import type { SubmenuItem, SubmenuItemWithChildren, NetworkSubmenuSection } from "../types";
|
||||
|
||||
export type SubmenuVariant = 'develop' | 'use-cases' | 'community' | 'network';
|
||||
|
||||
interface SubmenuProps {
|
||||
/** Which submenu variant to render */
|
||||
variant: SubmenuVariant;
|
||||
/** Whether this submenu is currently active (visible) */
|
||||
isActive: boolean;
|
||||
/** Whether this submenu is in closing animation */
|
||||
isClosing: boolean;
|
||||
/** Callback when submenu should close (e.g., Escape key) */
|
||||
onClose?: () => void;
|
||||
}
|
||||
|
||||
/** Get submenu data based on variant */
|
||||
function getSubmenuData(variant: SubmenuVariant) {
|
||||
switch (variant) {
|
||||
case 'develop': return developSubmenuData;
|
||||
case 'use-cases': return useCasesSubmenuData;
|
||||
case 'community': return communitySubmenuData;
|
||||
case 'network': return networkSubmenuData;
|
||||
}
|
||||
}
|
||||
|
||||
/** Get CSS modifier class for variant */
|
||||
function getVariantClass(variant: SubmenuVariant): string {
|
||||
if (variant === 'develop') return '';
|
||||
return `bds-submenu--${variant}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all focusable elements within a container
|
||||
*/
|
||||
function getFocusableElements(container: HTMLElement | null): HTMLElement[] {
|
||||
if (!container) return [];
|
||||
return Array.from(
|
||||
container.querySelectorAll<HTMLElement>('a[href], button:not([disabled]), [tabindex]:not([tabindex="-1"])')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the next nav item button after the current expanded one
|
||||
*/
|
||||
function getNextNavItem(): HTMLElement | null {
|
||||
const navItems = document.querySelectorAll<HTMLElement>('.bds-navbar__item');
|
||||
const currentIndex = Array.from(navItems).findIndex(item =>
|
||||
item.getAttribute('aria-expanded') === 'true'
|
||||
);
|
||||
if (currentIndex >= 0 && currentIndex < navItems.length - 1) {
|
||||
return navItems[currentIndex + 1];
|
||||
}
|
||||
// If at the last nav item, go to the first control button (search, etc.)
|
||||
const controls = document.querySelector<HTMLElement>('.bds-navbar__controls button, .bds-navbar__controls a');
|
||||
return controls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unified Submenu component.
|
||||
* Handles all submenu variants (develop, use-cases, community, network).
|
||||
* ARIA compliant with full keyboard navigation support.
|
||||
*/
|
||||
export function Submenu({ variant, isActive, isClosing, onClose }: SubmenuProps) {
|
||||
const submenuRef = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
// Handle keyboard events for accessibility
|
||||
const handleKeyDown = React.useCallback((event: KeyboardEvent) => {
|
||||
if (!isActive) return;
|
||||
|
||||
if (event.key === 'Escape') {
|
||||
event.preventDefault();
|
||||
onClose?.();
|
||||
// Return focus to the trigger button
|
||||
const triggerButton = document.querySelector<HTMLButtonElement>(
|
||||
`.bds-navbar__item[aria-expanded="true"]`
|
||||
);
|
||||
triggerButton?.focus();
|
||||
}
|
||||
|
||||
// Handle Tab at end of submenu - move to next nav item
|
||||
if (event.key === 'Tab' && !event.shiftKey) {
|
||||
const activeSubmenu = document.querySelector<HTMLElement>('.bds-submenu--active');
|
||||
const focusableElements = getFocusableElements(activeSubmenu);
|
||||
const lastFocusable = focusableElements[focusableElements.length - 1];
|
||||
|
||||
if (document.activeElement === lastFocusable) {
|
||||
event.preventDefault();
|
||||
onClose?.();
|
||||
const nextItem = getNextNavItem();
|
||||
nextItem?.focus();
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Shift+Tab at start of submenu - move back to trigger button
|
||||
if (event.key === 'Tab' && event.shiftKey) {
|
||||
const activeSubmenu = document.querySelector<HTMLElement>('.bds-submenu--active');
|
||||
const focusableElements = getFocusableElements(activeSubmenu);
|
||||
const firstFocusable = focusableElements[0];
|
||||
|
||||
if (document.activeElement === firstFocusable) {
|
||||
event.preventDefault();
|
||||
onClose?.();
|
||||
// Return focus to the trigger button
|
||||
const triggerButton = document.querySelector<HTMLButtonElement>(
|
||||
`.bds-navbar__item[aria-expanded="true"]`
|
||||
);
|
||||
triggerButton?.focus();
|
||||
}
|
||||
}
|
||||
}, [isActive, onClose]);
|
||||
|
||||
// Add keyboard event listener when submenu is active
|
||||
React.useEffect(() => {
|
||||
if (isActive) {
|
||||
document.addEventListener('keydown', handleKeyDown);
|
||||
return () => document.removeEventListener('keydown', handleKeyDown);
|
||||
}
|
||||
}, [isActive, handleKeyDown]);
|
||||
|
||||
// Network submenu needs special handling for theme-aware patterns
|
||||
if (variant === 'network') {
|
||||
return <NetworkSubmenuContent isActive={isActive} isClosing={isClosing} onClose={onClose} />;
|
||||
}
|
||||
|
||||
const data = getSubmenuData(variant);
|
||||
const classNames = [
|
||||
'bds-submenu',
|
||||
getVariantClass(variant),
|
||||
isActive ? 'bds-submenu--active' : '',
|
||||
isClosing ? 'bds-submenu--closing' : '',
|
||||
].filter(Boolean).join(' ');
|
||||
|
||||
// Standard two-column layout
|
||||
const leftItems = 'left' in data ? data.left : [];
|
||||
const rightItems = 'right' in data ? data.right : [];
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={submenuRef}
|
||||
className={classNames}
|
||||
role="menu"
|
||||
aria-hidden={!isActive}
|
||||
>
|
||||
<div className="bds-submenu__left">
|
||||
{leftItems.map((item: SubmenuItem | SubmenuItemWithChildren) => (
|
||||
<SubmenuSection key={item.label} item={item} />
|
||||
))}
|
||||
</div>
|
||||
<div className="bds-submenu__right">
|
||||
{rightItems.map((item: SubmenuItem | SubmenuItemWithChildren) => (
|
||||
<SubmenuSection key={item.label} item={item} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/** Network submenu with pattern images (same for light and dark mode) */
|
||||
function NetworkSubmenuContent({ isActive, isClosing, onClose }: { isActive: boolean; isClosing: boolean; onClose?: () => void }) {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
// Handle keyboard events for accessibility
|
||||
const handleKeyDown = React.useCallback((event: KeyboardEvent) => {
|
||||
if (!isActive) return;
|
||||
|
||||
if (event.key === 'Escape') {
|
||||
event.preventDefault();
|
||||
onClose?.();
|
||||
// Return focus to the trigger button
|
||||
const triggerButton = document.querySelector<HTMLButtonElement>(
|
||||
`.bds-navbar__item[aria-expanded="true"]`
|
||||
);
|
||||
triggerButton?.focus();
|
||||
}
|
||||
|
||||
// Handle Tab at end of submenu - move to next nav item
|
||||
if (event.key === 'Tab' && !event.shiftKey) {
|
||||
const activeSubmenu = document.querySelector<HTMLElement>('.bds-submenu--active');
|
||||
const focusableElements = getFocusableElements(activeSubmenu);
|
||||
const lastFocusable = focusableElements[focusableElements.length - 1];
|
||||
|
||||
if (document.activeElement === lastFocusable) {
|
||||
event.preventDefault();
|
||||
onClose?.();
|
||||
const nextItem = getNextNavItem();
|
||||
nextItem?.focus();
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Shift+Tab at start of submenu - move back to trigger button
|
||||
if (event.key === 'Tab' && event.shiftKey) {
|
||||
const activeSubmenu = document.querySelector<HTMLElement>('.bds-submenu--active');
|
||||
const focusableElements = getFocusableElements(activeSubmenu);
|
||||
const firstFocusable = focusableElements[0];
|
||||
|
||||
if (document.activeElement === firstFocusable) {
|
||||
event.preventDefault();
|
||||
onClose?.();
|
||||
// Return focus to the trigger button
|
||||
const triggerButton = document.querySelector<HTMLButtonElement>(
|
||||
`.bds-navbar__item[aria-expanded="true"]`
|
||||
);
|
||||
triggerButton?.focus();
|
||||
}
|
||||
}
|
||||
}, [isActive, onClose]);
|
||||
|
||||
// Add keyboard event listener when submenu is active
|
||||
React.useEffect(() => {
|
||||
if (isActive) {
|
||||
document.addEventListener('keydown', handleKeyDown);
|
||||
return () => document.removeEventListener('keydown', handleKeyDown);
|
||||
}
|
||||
}, [isActive, handleKeyDown]);
|
||||
|
||||
// Use same pattern images for both light and dark mode
|
||||
const patternImages = {
|
||||
lilac: resourcesIconPattern,
|
||||
green: insightsIconPattern,
|
||||
};
|
||||
|
||||
const classNames = [
|
||||
'bds-submenu',
|
||||
'bds-submenu--network',
|
||||
isActive ? 'bds-submenu--active' : '',
|
||||
isClosing ? 'bds-submenu--closing' : '',
|
||||
].filter(Boolean).join(' ');
|
||||
|
||||
return (
|
||||
<div className={classNames} role="menu" aria-hidden={!isActive}>
|
||||
{networkSubmenuData.map((section: NetworkSubmenuSection) => (
|
||||
<div key={section.label} className="bds-submenu__section">
|
||||
<a href={section.href} className="bds-submenu__tier1 bds-submenu__parent-link">
|
||||
<span className="bds-submenu__icon">
|
||||
<img src={navIcons[section.icon]} alt={translate(section.label)} />
|
||||
</span>
|
||||
<span className="bds-submenu__link bds-submenu__link--bold">
|
||||
{translate(section.label)}
|
||||
<span className="bds-submenu__arrow">
|
||||
<ArrowIcon animated />
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
<div className="bds-submenu__network-content">
|
||||
<div className="bds-submenu__tier2">
|
||||
{section.children.map((child) => (
|
||||
<a
|
||||
key={child.label}
|
||||
href={child.href}
|
||||
className="bds-submenu__sublink"
|
||||
target={child.href.startsWith('http') ? '_blank' : undefined}
|
||||
rel={child.href.startsWith('http') ? 'noopener noreferrer' : undefined}
|
||||
>
|
||||
{translate(child.label)}
|
||||
<span className="bds-submenu__sublink-arrow">
|
||||
<ArrowIcon animated={false} />
|
||||
</span>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
<div className="bds-submenu__pattern-container">
|
||||
<img src={patternImages[section.patternColor]} alt="" className="bds-submenu__pattern" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
70
@theme/components/Navbar/submenus/SubmenuSection.tsx
Normal file
70
@theme/components/Navbar/submenus/SubmenuSection.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import { useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
import { ArrowIcon } from "../icons";
|
||||
import { navIcons } from "../constants/icons";
|
||||
import { hasChildren, type SubmenuItem, type SubmenuItemWithChildren, type SubmenuItemBase } from "../types";
|
||||
|
||||
interface SubmenuSectionProps {
|
||||
/** The menu item data */
|
||||
item: SubmenuItem | SubmenuItemWithChildren | SubmenuItemBase;
|
||||
/** Whether to render children links (default: true) */
|
||||
showChildren?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unified submenu section component.
|
||||
* Renders a parent link with icon, and optionally child links if the item has them.
|
||||
*
|
||||
* Usage:
|
||||
* - For items that may or may not have children: <SubmenuSection item={item} />
|
||||
* - For parent-only rendering: <SubmenuSection item={item} showChildren={false} />
|
||||
*/
|
||||
export function SubmenuSection({ item, showChildren = true }: SubmenuSectionProps) {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
const itemHasChildren = hasChildren(item as SubmenuItem);
|
||||
const shouldShowChildren = showChildren && itemHasChildren;
|
||||
|
||||
return (
|
||||
<div className="bds-submenu__section">
|
||||
<a href={item.href} className="bds-submenu__tier1 bds-submenu__parent-link">
|
||||
<span className="bds-submenu__icon">
|
||||
<img src={navIcons[item.icon]} alt={translate(item.label)} />
|
||||
</span>
|
||||
<span className="bds-submenu__link bds-submenu__link--bold">
|
||||
{translate(item.label)}
|
||||
<span className="bds-submenu__arrow">
|
||||
<ArrowIcon animated />
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
{shouldShowChildren && (
|
||||
<div className="bds-submenu__tier2">
|
||||
{(item as SubmenuItemWithChildren).children.map((child) => (
|
||||
<a
|
||||
key={child.label}
|
||||
href={child.href}
|
||||
className="bds-submenu__sublink"
|
||||
target={child.href.startsWith('http') ? '_blank' : undefined}
|
||||
rel={child.href.startsWith('http') ? 'noopener noreferrer' : undefined}
|
||||
>
|
||||
{translate(child.label)}
|
||||
<span className="bds-submenu__sublink-arrow">
|
||||
<ArrowIcon animated={false} />
|
||||
</span>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Backwards-compatible aliases (all use the unified SubmenuSection)
|
||||
export const SubmenuParentOnly = ({ item }: { item: SubmenuItemBase }) => (
|
||||
<SubmenuSection item={item} showChildren={false} />
|
||||
);
|
||||
|
||||
export const SubmenuWithChildren = ({ item }: { item: SubmenuItemWithChildren }) => (
|
||||
<SubmenuSection item={item} showChildren={true} />
|
||||
);
|
||||
|
||||
16
@theme/components/Navbar/submenus/UseCasesSubmenu.tsx
Normal file
16
@theme/components/Navbar/submenus/UseCasesSubmenu.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Submenu } from "./Submenu";
|
||||
|
||||
interface UseCasesSubmenuProps {
|
||||
isActive: boolean;
|
||||
isClosing: boolean;
|
||||
onClose?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Desktop Use Cases Submenu Component.
|
||||
* Wrapper for unified Submenu component with 'use-cases' variant.
|
||||
*/
|
||||
export function UseCasesSubmenu({ isActive, isClosing, onClose }: UseCasesSubmenuProps) {
|
||||
return <Submenu variant="use-cases" isActive={isActive} isClosing={isClosing} onClose={onClose} />;
|
||||
}
|
||||
|
||||
12
@theme/components/Navbar/submenus/index.ts
Normal file
12
@theme/components/Navbar/submenus/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
// Unified submenu component
|
||||
export { Submenu, type SubmenuVariant } from './Submenu';
|
||||
|
||||
// Variant-specific wrappers (all use Submenu internally)
|
||||
export { DevelopSubmenu } from './DevelopSubmenu';
|
||||
export { UseCasesSubmenu } from './UseCasesSubmenu';
|
||||
export { CommunitySubmenu } from './CommunitySubmenu';
|
||||
export { NetworkSubmenu } from './NetworkSubmenu';
|
||||
|
||||
// Reusable submenu section component
|
||||
export { SubmenuSection, SubmenuParentOnly, SubmenuWithChildren } from './SubmenuSection';
|
||||
|
||||
42
@theme/components/Navbar/types.ts
Normal file
42
@theme/components/Navbar/types.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
// Types for submenu data structures
|
||||
|
||||
export interface SubmenuChild {
|
||||
label: string;
|
||||
href: string;
|
||||
active?: boolean;
|
||||
}
|
||||
|
||||
export interface SubmenuItemBase {
|
||||
label: string;
|
||||
href: string;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
export interface SubmenuItemWithChildren extends SubmenuItemBase {
|
||||
children: SubmenuChild[];
|
||||
}
|
||||
|
||||
export type SubmenuItem = SubmenuItemBase | SubmenuItemWithChildren;
|
||||
|
||||
// Network submenu section with decorative images
|
||||
export interface NetworkSubmenuSection {
|
||||
label: string;
|
||||
href: string;
|
||||
icon: string;
|
||||
children: SubmenuChild[];
|
||||
patternColor: 'lilac' | 'green';
|
||||
}
|
||||
|
||||
// Nav item type
|
||||
export interface NavItem {
|
||||
label: string;
|
||||
labelTranslationKey: string;
|
||||
href: string;
|
||||
hasSubmenu: boolean;
|
||||
}
|
||||
|
||||
// Type guard to check if item has children
|
||||
export function hasChildren(item: SubmenuItem): item is SubmenuItemWithChildren {
|
||||
return 'children' in item && Array.isArray((item as SubmenuItemWithChildren).children);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ export function XRPLCard(props: XRPLCardProps) {
|
||||
<p className="card-text">{props.body}</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="card-footer"> </div>
|
||||
{/* <div className="card-footer"> </div> */}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
178
COLOR-MIGRATION-SUMMARY.md
Normal file
178
COLOR-MIGRATION-SUMMARY.md
Normal file
@@ -0,0 +1,178 @@
|
||||
# Color System Migration Summary
|
||||
|
||||
**Date:** October 21, 2025
|
||||
**Source:** [XRPL.org Design Tokens - Figma](https://figma.com/design/zRyhXG4hRP3Lk3B6Owr3eo/XRPL.org-Design-Tokens)
|
||||
|
||||
## Migration Strategy: Clean Migration
|
||||
|
||||
The old 10-level color scale (100-1000) has been completely migrated to a new 5-level scale (100-500). All references in the codebase have been updated, and backward compatibility aliases have been removed for a clean implementation.
|
||||
|
||||
**Mapping Applied:**
|
||||
```
|
||||
Old System → New System
|
||||
100 → 100 (lightest)
|
||||
200 → 100
|
||||
300 → 200
|
||||
400 → 200
|
||||
500 → 300 (mid-tone, default)
|
||||
600 → 300
|
||||
700 → 400
|
||||
800 → 400
|
||||
900 → 500 (darkest)
|
||||
1000 → 500
|
||||
```
|
||||
|
||||
**Migration Approach:**
|
||||
1. All color usages (600-1000) were found and replaced with their new equivalents (300-500)
|
||||
2. Backward compatibility aliases were removed from `_colors.scss`
|
||||
3. Only 100-500 design tokens remain for each color family
|
||||
|
||||
## Color Families Updated
|
||||
|
||||
### Primary Colors
|
||||
|
||||
#### Gray (Neutral) ⏸️ NOT UPDATED
|
||||
- **Status:** Original values retained - design tokens not ready
|
||||
- **Current values:** #FCFCFD, #F5F5F7, #E0E0E1, #C1C1C2, #A2A2A4, #838386, #454549, #343437, #232325, #111112
|
||||
- **Note:** Gray/Neutral design tokens will be migrated in a future update
|
||||
|
||||
#### Green ✅
|
||||
- **New Design Tokens:** #EAFCF1, #70EE97, #21E46B, #0DAA3E, #078139
|
||||
- **Variables:** `$green-100` through `$green-500` only
|
||||
- **Migrated:** 14 file references updated
|
||||
- **Special:** `$apex-2023-green` (#00FF76) retained
|
||||
|
||||
#### Lilac (Primary) ✅ *replaces blue-purple*
|
||||
- **New Design Tokens:** #F2EDFF, #D9CAFF, #C0A7FF, #7649E3, #5429A1
|
||||
- **Variables:** `$lilac-100` through `$lilac-500` only
|
||||
- **Legacy aliases:** `$blue-purple-100` through `$blue-purple-500` map to lilac (600-900 removed)
|
||||
- **Migrated:** 16 file references updated
|
||||
- **Note:** This is a new color name in the design system
|
||||
|
||||
### Secondary Colors
|
||||
|
||||
#### Red ✅ *NEW*
|
||||
- **New Design Tokens:** #FDECE7, #F27A66, #F0643A, #DA4518, #A22514
|
||||
- **Variables:** `$red-100` through `$red-500` only
|
||||
- **Note:** This is a completely new color family added to the design system
|
||||
|
||||
#### Pink ✅ *replaces magenta*
|
||||
- **New Design Tokens:** #FDF1F4, #F2B5C3, #F18DA5, #FF577F, #DC466F
|
||||
- **Variables:** `$pink-100` through `$pink-500` only
|
||||
- **Legacy aliases:** `$magenta-100` through `$magenta-500` map to pink (600-900 removed)
|
||||
- **Migrated:** 7 file references updated
|
||||
|
||||
#### Blue ✅
|
||||
- **New Design Tokens:** #EDF4FF, #93BFF1, #428CFF, #0179E7, #0A4DC0
|
||||
- **Variables:** `$blue-100` through `$blue-500` only
|
||||
- **Migrated:** 8 file references updated
|
||||
- **Special:** `$accent-blue-90` (#001133) retained
|
||||
|
||||
#### Yellow ✅
|
||||
- **New Design Tokens:** #F3F1EB, #E6F1A7, #DBF15E, #E1DB26, #D4C02D
|
||||
- **Variables:** `$yellow-100` through `$yellow-500` only
|
||||
- **Migrated:** 11 file references updated
|
||||
|
||||
## Colors Retained (No Design Token Replacement)
|
||||
|
||||
### Orange
|
||||
- **Status:** Legacy values retained
|
||||
- **Values:** #FFEEE5, #FFCCB2, #FFAA80, #FF884B, #FF6719, #E54D00, #B23C00, #802B00, #4C1A00
|
||||
- **Reason:** No corresponding design token in new system
|
||||
|
||||
### Red-purple
|
||||
- **Status:** Legacy values retained
|
||||
- **Values:** #FBE5FF, #F2B2FF, #EA80FF, #E24CFF, #D919FF, #C000E5, #9500B2, #6B0080, #40004C
|
||||
- **Reason:** No corresponding design token in new system
|
||||
|
||||
### Special Event Colors
|
||||
- `$apex-2023-green: #00FF76`
|
||||
- `$token-2049-purple: #410bb9`
|
||||
- `$accent-blue-90: #001133`
|
||||
|
||||
## Bootstrap & Component Colors
|
||||
|
||||
All Bootstrap theme variables remain functional:
|
||||
- `$primary` → `$purple` (now `$lilac-400`)
|
||||
- `$secondary` → `$gray-200`
|
||||
- `$success` → `$green-500`
|
||||
- `$info` → `$blue-500`
|
||||
- `$warning` → `$yellow-500`
|
||||
- `$danger` → `$magenta-500` (now `$pink-500`)
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
**Removed Variables:**
|
||||
- All color variables from 600-1000 have been removed for: Green, Blue, Lilac, Pink, Red, Yellow
|
||||
- `$blue-purple-600` through `$blue-purple-900` removed (use 100-500)
|
||||
- `$magenta-600` through `$magenta-900` removed (use 100-500)
|
||||
|
||||
**No Impact:**
|
||||
- All usages in the codebase have been updated
|
||||
- Legacy color name aliases maintained (100-500 only):
|
||||
- `$blue-purple-100` through `$blue-purple-500` → maps to `$lilac-*`
|
||||
- `$magenta-100` through `$magenta-500` → maps to `$pink-*`
|
||||
|
||||
## Color Name Changes
|
||||
|
||||
| Old Name | New Name | Reason |
|
||||
|----------|----------|--------|
|
||||
| `blue-purple-*` | `lilac-*` | Design system rebranding |
|
||||
| `magenta-*` | `pink-*` | Design system rebranding |
|
||||
| N/A | `red-*` | New color family added |
|
||||
|
||||
## Usage Recommendations
|
||||
|
||||
### Current Best Practices
|
||||
Use the new 5-level design tokens (100-500):
|
||||
```scss
|
||||
// Primary colors
|
||||
color: $gray-300; // Gray (not yet migrated - still uses old values)
|
||||
color: $green-300; // Default green
|
||||
color: $lilac-400; // Primary purple
|
||||
|
||||
// Secondary colors
|
||||
color: $red-300; // Default red
|
||||
color: $pink-300; // Default pink
|
||||
color: $blue-300; // Default blue
|
||||
color: $yellow-300; // Default yellow
|
||||
```
|
||||
|
||||
### Legacy Aliases Still Available
|
||||
```scss
|
||||
// These legacy names work (100-500 only)
|
||||
color: $blue-purple-400; // Same as $lilac-400
|
||||
color: $magenta-300; // Same as $pink-300
|
||||
```
|
||||
|
||||
## Files Modified
|
||||
|
||||
- `styles/_colors.scss` - Complete color system update
|
||||
|
||||
## Validation Status
|
||||
|
||||
✅ All SCSS variables resolve correctly
|
||||
✅ No linter errors
|
||||
✅ Bootstrap theme colors functional
|
||||
✅ All old color references (600-1000) removed from codebase
|
||||
✅ Special event colors preserved
|
||||
⏸️ Gray/Neutral colors - pending future update
|
||||
|
||||
## Migration Statistics
|
||||
|
||||
**Files Updated:** 11 SCSS files
|
||||
- `styles/_colors.scss` - Color definitions cleaned up
|
||||
- `styles/light/_light-theme.scss` - 11 color references updated
|
||||
- `styles/_status-labels.scss` - 39 color references updated
|
||||
- `styles/_diagrams.scss` - 6 color references updated
|
||||
- `styles/_code-tabs.scss` - 2 color references updated
|
||||
- `styles/_content.scss` - 1 color reference updated
|
||||
- `styles/_buttons.scss` - 7 color references updated
|
||||
- `styles/_pages.scss` - 3 color references updated
|
||||
- `styles/_blog.scss` - 2 color references updated
|
||||
- `styles/_feedback.scss` - 2 color references updated
|
||||
- `styles/_rpc-tool.scss` - 1 color reference updated
|
||||
- `styles/_landings.scss` - 1 color reference updated
|
||||
|
||||
**Total Color References Updated:** 75+ instances
|
||||
|
||||
154
CSS-OPTIMIZATION-SUMMARY.md
Normal file
154
CSS-OPTIMIZATION-SUMMARY.md
Normal file
@@ -0,0 +1,154 @@
|
||||
# CSS Optimization - Implementation Summary
|
||||
|
||||
## ✅ Successfully Completed
|
||||
|
||||
The CSS build pipeline has been modernized with industry-standard optimization tools, resulting in significant performance improvements.
|
||||
|
||||
## Results
|
||||
|
||||
### Bundle Size Improvements
|
||||
|
||||
\`\`\`
|
||||
=== CSS Bundle Comparison ===
|
||||
|
||||
Master (Bootstrap 4):
|
||||
Uncompressed: 405.09 KB
|
||||
Gzipped: 63.44 KB
|
||||
|
||||
This Branch BEFORE Optimization (Bootstrap 5):
|
||||
Uncompressed: 486.64 KB
|
||||
Gzipped: 71.14 KB
|
||||
|
||||
This Branch AFTER Optimization (Bootstrap 5 + PurgeCSS):
|
||||
Uncompressed: 280.92 KB ✅ 42% smaller
|
||||
Gzipped: 43.32 KB ✅ 39% smaller (network transfer)
|
||||
\`\`\`
|
||||
|
||||
### Key Improvements
|
||||
|
||||
| Metric | Before | After | Improvement |
|
||||
|--------|--------|-------|-------------|
|
||||
| **Network Transfer (Gzipped)** | 71.14 KB | 43.32 KB | **39% smaller** |
|
||||
| **Uncompressed Size** | 486.64 KB | 280.92 KB | **42% smaller** |
|
||||
| **CSS Selectors** | 5,423 | 2,681 | **51% fewer** |
|
||||
| **DevTools Filter Performance** | ~60 seconds | <1 second | **98% faster** |
|
||||
|
||||
### Real-World Impact
|
||||
|
||||
- **Page Load:** 40% faster CSS download on 3G connections
|
||||
- **Developer Experience:** DevTools CSS filtering is now instant (<1s vs 60s)
|
||||
- **Bandwidth Savings:** ~28 KB less per page load
|
||||
- **Maintainability:** Modern tooling with source maps in development
|
||||
|
||||
## What Was Implemented
|
||||
|
||||
### 1. Modern Build Pipeline
|
||||
|
||||
- **Upgraded Sass** from 1.26.10 (2020) → 1.93.2 (latest)
|
||||
- **Added PostCSS** with optimization plugins:
|
||||
- **PurgeCSS** - Removes unused CSS selectors
|
||||
- **Autoprefixer** - Browser compatibility
|
||||
- **cssnano** - Advanced minification
|
||||
|
||||
### 2. Build Scripts
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"build-css": "Production build with full optimization",
|
||||
"build-css-dev": "Development build with source maps",
|
||||
"build-css-watch": "Watch mode for continuous compilation",
|
||||
"analyze-css": "Bundle analysis tool"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. PurgeCSS Configuration
|
||||
|
||||
- Scans all `.tsx`, `.md`, `.yaml`, `.html` files for class names
|
||||
- Intelligent safelist for dynamically-added classes
|
||||
- Preserves Bootstrap JS components, CodeMirror, custom tools
|
||||
- Only runs in production (dev builds are fast)
|
||||
|
||||
### 4. CSS Analysis Tool
|
||||
|
||||
Created `scripts/analyze-css.js` to monitor:
|
||||
- Bundle size and composition
|
||||
- Bootstrap component usage
|
||||
- Optimization opportunities
|
||||
- Before/after comparisons
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
### New Files
|
||||
- `postcss.config.cjs` - PostCSS and PurgeCSS configuration
|
||||
- `scripts/analyze-css.js` - CSS bundle analysis tool
|
||||
- `CSS-OPTIMIZATION.md` - Comprehensive optimization guide
|
||||
- `CSS-OPTIMIZATION-SUMMARY.md` - This summary
|
||||
|
||||
### Modified Files
|
||||
- `package.json` - Updated dependencies and build scripts
|
||||
- `styles/README.md` - Updated build documentation
|
||||
|
||||
### Configuration Files
|
||||
All configuration files include extensive inline documentation explaining decisions and patterns.
|
||||
|
||||
## Usage
|
||||
|
||||
### For Production
|
||||
```bash
|
||||
npm run build-css # Full optimization
|
||||
npm run analyze-css # Check results
|
||||
```
|
||||
|
||||
### For Development
|
||||
```bash
|
||||
npm run build-css:dev # Fast build with source maps
|
||||
npm run build-css:watch # Auto-rebuild on changes
|
||||
```
|
||||
|
||||
## Backward Compatibility
|
||||
|
||||
✅ **No breaking changes** - All existing styles are preserved
|
||||
✅ Visual appearance is identical
|
||||
✅ All Bootstrap components still work
|
||||
✅ Dynamic classes are safelisted
|
||||
|
||||
## Documentation
|
||||
|
||||
- **`styles/README.md`** - Build process and troubleshooting
|
||||
- **`CSS-OPTIMIZATION.md`** - Detailed implementation guide
|
||||
- **`postcss.config.cjs`** - Inline configuration documentation
|
||||
|
||||
## Maintenance
|
||||
|
||||
### Adding New Styles
|
||||
|
||||
1. Create `_component.scss` file
|
||||
2. Import in `xrpl.scss`
|
||||
3. Add dynamic classes to safelist if needed
|
||||
4. Test: `npm run build-css:dev` and `npm run build-css`
|
||||
5. Analyze: `npm run analyze-css`
|
||||
|
||||
### Troubleshooting Missing Styles
|
||||
|
||||
If styles are missing in production:
|
||||
1. Check if class is added dynamically
|
||||
2. Add pattern to safelist in `postcss.config.cjs`
|
||||
3. Rebuild with `npm run build-css`
|
||||
|
||||
## Next Steps (Optional Future Optimizations)
|
||||
|
||||
1. **Code Splitting** - Separate vendor CSS from custom styles
|
||||
2. **Critical CSS** - Extract above-the-fold styles
|
||||
3. **Bootstrap Customization** - Import only needed components
|
||||
4. **CSS Modules** - Component-scoped styles for React pages
|
||||
|
||||
## Conclusion
|
||||
|
||||
The CSS optimization is complete and working perfectly. The bundle size has been reduced by 42% (uncompressed) and 39% (gzipped), resulting in faster page loads and dramatically improved developer experience.
|
||||
|
||||
**Status: ✅ Ready for Production**
|
||||
|
||||
---
|
||||
*Last Updated: October 2025*
|
||||
381
CSS-OPTIMIZATION.md
Normal file
381
CSS-OPTIMIZATION.md
Normal file
@@ -0,0 +1,381 @@
|
||||
# CSS Optimization Guide
|
||||
|
||||
## Overview
|
||||
|
||||
This document describes the CSS optimization implementation for the XRPL Dev Portal, including the rationale, implementation details, performance improvements, and maintenance guidelines.
|
||||
|
||||
## The Problem
|
||||
|
||||
### Before Optimization
|
||||
|
||||
The dev portal was serving a **486 KB** minified CSS bundle that included:
|
||||
|
||||
- **Entire Bootstrap 5.3.8 framework** (~200+ KB)
|
||||
- Thousands of unused CSS selectors
|
||||
- No tree-shaking or dead code elimination
|
||||
- All styles loaded on every page, regardless of usage
|
||||
- **1-minute lag** in Chrome DevTools when filtering CSS
|
||||
|
||||
#### Impact
|
||||
|
||||
- **Developer Experience:** DevTools filter took 60+ seconds to respond
|
||||
- **Page Performance:** 486 KB CSS downloaded on every page load
|
||||
- **Build Process:** Outdated Sass 1.26.10 (from 2020)
|
||||
- **Debugging:** No source maps, even in development
|
||||
|
||||
### Analysis Results
|
||||
|
||||
Initial analysis showed:
|
||||
|
||||
```
|
||||
Bundle Size: 486.64 KB
|
||||
Total Selectors: 5,423
|
||||
Unique Selectors: 4,678
|
||||
|
||||
Bootstrap Component Usage:
|
||||
- Pagination: 998 usages
|
||||
- Cards: 428 usages
|
||||
- Grid System: 253 usages
|
||||
- ...but also...
|
||||
- Toast: 8 usages
|
||||
- Spinner: 8 usages
|
||||
- Accordion: 0 usages (unused!)
|
||||
```
|
||||
|
||||
## The Solution
|
||||
|
||||
### Modern Build Pipeline
|
||||
|
||||
Implemented a three-stage optimization pipeline:
|
||||
|
||||
```
|
||||
SCSS → Sass Compiler → PostCSS → Optimized CSS
|
||||
│
|
||||
├─ PurgeCSS (removes unused)
|
||||
├─ Autoprefixer (adds vendor prefixes)
|
||||
└─ cssnano (minifies)
|
||||
```
|
||||
|
||||
### Key Technologies
|
||||
|
||||
1. **Sass (latest)** - Modern SCSS compilation with better performance
|
||||
2. **PostCSS** - Industry-standard CSS processing
|
||||
3. **PurgeCSS** - Intelligent unused CSS removal
|
||||
4. **Autoprefixer** - Browser compatibility
|
||||
5. **cssnano** - Advanced minification
|
||||
|
||||
## Implementation
|
||||
|
||||
### 1. Dependency Upgrades
|
||||
|
||||
```json
|
||||
{
|
||||
"devDependencies": {
|
||||
"sass": "^1.93.2", // was 1.26.10
|
||||
"postcss": "^8.5.6",
|
||||
"postcss-cli": "^11.0.1",
|
||||
"@fullhuman/postcss-purgecss": "^7.0.2",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"cssnano": "^7.1.1"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Build Scripts
|
||||
|
||||
Created separate development and production builds:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"build-css": "Production build with full optimization",
|
||||
"build-css:dev": "Development build with source maps",
|
||||
"build-css:watch": "Watch mode for continuous compilation",
|
||||
"analyze-css": "node scripts/analyze-css.js"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Production Build:**
|
||||
- ✅ Full PurgeCSS optimization
|
||||
- ✅ Minified and compressed
|
||||
- ✅ Autoprefixed
|
||||
- ❌ No source maps
|
||||
|
||||
**Development Build:**
|
||||
- ✅ Source maps for debugging
|
||||
- ✅ Autoprefixed
|
||||
- ❌ No PurgeCSS (faster builds)
|
||||
- ❌ Not minified (readable)
|
||||
|
||||
### 3. PurgeCSS Configuration
|
||||
|
||||
Created `postcss.config.cjs` with intelligent safelist:
|
||||
|
||||
```javascript
|
||||
// Content paths - scan these for class names
|
||||
content: [
|
||||
'./**/*.tsx',
|
||||
'./**/*.md',
|
||||
'./**/*.yaml',
|
||||
'./**/*.html',
|
||||
'./static/js/**/*.js',
|
||||
]
|
||||
|
||||
// Safelist - preserve these classes
|
||||
safelist: {
|
||||
standard: [
|
||||
'html', 'body', 'light', 'dark',
|
||||
/^show$/, /^active$/, /^disabled$/,
|
||||
],
|
||||
deep: [
|
||||
/dropdown-menu/, /modal-backdrop/,
|
||||
/cm-/, /CodeMirror/, // Third-party
|
||||
/rpc-tool/, /websocket/, // Custom components
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
**Safelist Strategy:**
|
||||
- **Standard:** State classes added by JavaScript
|
||||
- **Deep:** Component patterns (keeps parent and children)
|
||||
- **Greedy:** Attribute-based matching
|
||||
|
||||
### 4. Analysis Tool
|
||||
|
||||
Created `scripts/analyze-css.js` to track optimization:
|
||||
|
||||
- Bundle size metrics
|
||||
- Selector counts
|
||||
- Bootstrap component usage
|
||||
- Custom pattern detection
|
||||
- Optimization recommendations
|
||||
|
||||
## Results
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
| Metric | Before | After | Improvement |
|
||||
|--------|--------|-------|-------------|
|
||||
| **Bundle Size (Uncompressed)** | 486.64 KB | 280.92 KB | **42% smaller** |
|
||||
| **Bundle Size (Gzipped)** | 71.14 KB | 43.32 KB | **39% smaller** |
|
||||
| **Total Selectors** | 5,423 | 2,681 | **51% fewer** |
|
||||
| **Unique Selectors** | 4,678 | 2,167 | **54% fewer** |
|
||||
| **DevTools Filter** | ~60 seconds | <1 second | **98% faster** |
|
||||
| **Download Time (3G)** | ~2.0s | ~1.2s | **40% faster** |
|
||||
|
||||
**Note:** Gzipped size is what actually gets transmitted over the network, representing the real-world bandwidth savings.
|
||||
|
||||
### Bootstrap Component Optimization
|
||||
|
||||
| Component | Before | After | Reduction |
|
||||
|-----------|--------|-------|-----------|
|
||||
| Pagination | 998 | 831 | 17% |
|
||||
| Cards | 428 | 306 | 29% |
|
||||
| Grid System | 253 | 94 | 63% |
|
||||
| Badge | 253 | 0 | 100% (unused) |
|
||||
| Navbar | 171 | 78 | 54% |
|
||||
| Buttons | 145 | 77 | 47% |
|
||||
| Forms | 179 | 70 | 61% |
|
||||
|
||||
### Developer Experience
|
||||
|
||||
**Before:**
|
||||
```
|
||||
Build time: 5-10 seconds
|
||||
DevTools CSS filter: 60 seconds
|
||||
Debugging: No source maps
|
||||
```
|
||||
|
||||
**After:**
|
||||
```
|
||||
Production build: 8-12 seconds
|
||||
Development build: 3-5 seconds (no PurgeCSS)
|
||||
DevTools CSS filter: <1 second
|
||||
Debugging: Source maps in dev mode
|
||||
```
|
||||
|
||||
## Maintenance
|
||||
|
||||
### Adding New Styles
|
||||
|
||||
When adding new component styles:
|
||||
|
||||
1. **Create the SCSS file:**
|
||||
```scss
|
||||
// styles/_my-component.scss
|
||||
.my-component {
|
||||
// styles here
|
||||
}
|
||||
```
|
||||
|
||||
2. **Import in xrpl.scss:**
|
||||
```scss
|
||||
@import "_my-component.scss";
|
||||
```
|
||||
|
||||
3. **If using dynamic classes, update safelist:**
|
||||
```javascript
|
||||
// postcss.config.cjs
|
||||
deep: [
|
||||
/my-component/, // Keeps all .my-component-* classes
|
||||
]
|
||||
```
|
||||
|
||||
4. **Test both builds:**
|
||||
```bash
|
||||
npm run build-css:dev # Test development build
|
||||
npm run build-css # Test production build
|
||||
npm run analyze-css # Check bundle size impact
|
||||
```
|
||||
|
||||
### Troubleshooting Missing Styles
|
||||
|
||||
If styles are missing after a production build:
|
||||
|
||||
1. **Identify the missing class:**
|
||||
```bash
|
||||
# Search for class usage in codebase
|
||||
grep -r "missing-class" .
|
||||
```
|
||||
|
||||
2. **Check if it's dynamically added:**
|
||||
- Bootstrap JavaScript components
|
||||
- React state-based classes
|
||||
- Third-party library classes
|
||||
|
||||
3. **Add to PurgeCSS safelist:**
|
||||
```javascript
|
||||
// postcss.config.cjs
|
||||
safelist: {
|
||||
deep: [
|
||||
/missing-class/, // Preserve this pattern
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
4. **Rebuild and verify:**
|
||||
```bash
|
||||
npm run build-css
|
||||
npm run analyze-css
|
||||
```
|
||||
|
||||
### Monitoring Bundle Size
|
||||
|
||||
Run the analysis tool regularly:
|
||||
|
||||
```bash
|
||||
npm run analyze-css
|
||||
```
|
||||
|
||||
**Watch for:**
|
||||
- Bundle size > 350 KB (indicates regression)
|
||||
- Components with 0 usages (can be removed from Bootstrap import)
|
||||
- Significant selector count increases
|
||||
|
||||
### Future Optimizations
|
||||
|
||||
Potential next steps for further optimization:
|
||||
|
||||
1. **Code Splitting**
|
||||
- Split vendor CSS (Bootstrap) from custom styles
|
||||
- Lazy-load page-specific styles
|
||||
- Critical CSS extraction
|
||||
|
||||
2. **Bootstrap Customization**
|
||||
- Import only needed Bootstrap components
|
||||
- Remove unused variables and mixins
|
||||
- Custom Bootstrap build
|
||||
|
||||
3. **Component-Level CSS**
|
||||
- CSS Modules for page components
|
||||
- CSS-in-JS for dynamic styles
|
||||
- Scoped styles per route
|
||||
|
||||
4. **Advanced Compression**
|
||||
- Brotli compression (88% ratio vs 76% gzip)
|
||||
- CSS splitting by media queries
|
||||
- HTTP/2 server push for critical CSS
|
||||
|
||||
## Migration Notes
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
**None** - This optimization is backward-compatible. All existing classes and styles are preserved.
|
||||
|
||||
### Testing Checklist
|
||||
|
||||
When testing the optimization:
|
||||
|
||||
- [ ] Homepage loads correctly
|
||||
- [ ] Documentation pages display properly
|
||||
- [ ] Blog posts render correctly
|
||||
- [ ] Dev tools (RPC tool, WebSocket tool) function
|
||||
- [ ] Navigation menus work
|
||||
- [ ] Dropdowns and modals open correctly
|
||||
- [ ] Forms are styled properly
|
||||
- [ ] Code syntax highlighting works
|
||||
- [ ] Print styles work
|
||||
- [ ] Light/dark theme switching works
|
||||
|
||||
### Rollback Procedure
|
||||
|
||||
If issues are found:
|
||||
|
||||
1. **Temporarily revert to old build:**
|
||||
```bash
|
||||
# In package.json, change build-css to:
|
||||
"build-css": "sass --load-path styles/scss styles/xrpl.scss ./static/css/devportal2024-v1.css --style compressed --no-source-map"
|
||||
```
|
||||
|
||||
2. **Rebuild:**
|
||||
```bash
|
||||
npm run build-css
|
||||
```
|
||||
|
||||
3. **Report the issue** with:
|
||||
- Missing class names
|
||||
- Page where issue appears
|
||||
- Expected vs actual behavior
|
||||
|
||||
## Resources
|
||||
|
||||
### Documentation
|
||||
|
||||
- [PurgeCSS Documentation](https://purgecss.com/)
|
||||
- [PostCSS Documentation](https://postcss.org/)
|
||||
- [Sass Documentation](https://sass-lang.com/)
|
||||
- [Bootstrap Customization](https://getbootstrap.com/docs/5.3/customize/sass/)
|
||||
|
||||
### Tools
|
||||
|
||||
- `npm run build-css` - Production build
|
||||
- `npm run build-css:dev` - Development build
|
||||
- `npm run build-css:watch` - Watch mode
|
||||
- `npm run analyze-css` - Bundle analysis
|
||||
|
||||
### Files
|
||||
|
||||
- `styles/README.md` - Build process documentation
|
||||
- `postcss.config.cjs` - PostCSS and PurgeCSS configuration
|
||||
- `scripts/analyze-css.js` - Bundle analysis tool
|
||||
- `package.json` - Build scripts
|
||||
|
||||
## Conclusion
|
||||
|
||||
This optimization reduces the CSS bundle by 42% (486 KB → 281 KB), dramatically improving both developer experience and end-user performance. The implementation uses industry-standard tools and maintains full backward compatibility while providing a foundation for future optimizations.
|
||||
|
||||
**Key Takeaways:**
|
||||
- ✅ 42% smaller uncompressed CSS bundle (486 KB → 281 KB)
|
||||
- ✅ 39% smaller gzipped bundle (71 KB → 43 KB network transfer)
|
||||
- ✅ 98% faster DevTools filtering (60s → <1s)
|
||||
- ✅ Modern build tooling (Sass + PostCSS + PurgeCSS)
|
||||
- ✅ Source maps in development mode
|
||||
- ✅ Backward compatible - no breaking changes
|
||||
- ✅ Well documented and maintainable
|
||||
|
||||
---
|
||||
|
||||
*Last updated: October 2025*
|
||||
*Contributors: CSS Optimization Initiative*
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="send-xrp-modal-label">Send XRP</h1>
|
||||
<h1 class="modal-title subhead-sm-r" id="send-xrp-modal-label">Send XRP</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="send-xrp-modal-label">Send XRP</h1>
|
||||
<h1 class="modal-title subhead-sm-r" id="send-xrp-modal-label">Send XRP</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
@@ -24,14 +24,6 @@ export default function History() {
|
||||
return (
|
||||
<div className="landing">
|
||||
<div className="overflow-hidden">
|
||||
<div className="position-relative">
|
||||
<img
|
||||
alt="background orange waves"
|
||||
src={require("../static/img/backgrounds/history-orange.svg")}
|
||||
className="landing-bg"
|
||||
id="history-orange"
|
||||
/>
|
||||
</div>
|
||||
<section className="py-26 text-center">
|
||||
<div className="col-lg-5 mx-auto text-center">
|
||||
<div className="d-flex flex-column-reverse">
|
||||
@@ -61,13 +53,6 @@ export default function History() {
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<div className="position-relative d-none-sm">
|
||||
<img
|
||||
alt="background purple waves"
|
||||
src={require("../static/img/backgrounds/history-purple.svg")}
|
||||
id="history-purple"
|
||||
/>
|
||||
</div>
|
||||
<div className="container-new marketing-wrapper">
|
||||
<section className="row mb-60">
|
||||
<div className="timeline">
|
||||
|
||||
@@ -32,14 +32,6 @@ export default function Impact() {
|
||||
return (
|
||||
<div className="landing page-impact">
|
||||
<div className="overflow-hidden">
|
||||
<div className="position-relative d-none-sm">
|
||||
<img
|
||||
alt="purple waves"
|
||||
src={require("../static/img/backgrounds/community-purple.svg")}
|
||||
className="landing-bg"
|
||||
id="impact-purple"
|
||||
/>
|
||||
</div>
|
||||
<section className="container-new py-26 text-lg-center">
|
||||
<div className="p-0 col-lg-8 mx-lg-auto">
|
||||
<div className="d-flex flex-column-reverse">
|
||||
@@ -52,13 +44,6 @@ export default function Impact() {
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div className="position-relative d-none-sm">
|
||||
<img
|
||||
alt="green waves"
|
||||
src={require("../static/img/backgrounds/home-green.svg")}
|
||||
id="impact-green"
|
||||
/>
|
||||
</div>
|
||||
{/* World map */}
|
||||
<section className="container-new py-10">
|
||||
<div className="col-sm-10 col-lg-6 offset-md-3 p-10-until-sm pl-0-sm pr-0-sm">
|
||||
@@ -133,16 +118,6 @@ export default function Impact() {
|
||||
{/* Card */}
|
||||
<section className="container-new py-26">
|
||||
<div className="col-md-6 offset-md-3 p-6-sm p-10-until-sm br-8 cta-card">
|
||||
<img
|
||||
alt="purple waves"
|
||||
src={require("../static/img/backgrounds/cta-community-purple.svg")}
|
||||
className="cta cta-top-left"
|
||||
/>
|
||||
<img
|
||||
alt="green waves"
|
||||
src={require("../static/img/backgrounds/cta-calculator-green.svg")}
|
||||
className="cta cta-bottom-right"
|
||||
/>
|
||||
<div className="z-index-1 position-relative">
|
||||
<div className="d-flex flex-column-reverse">
|
||||
<h2 className="h4 h2-sm mb-10-until-sm mb-8-sm">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as React from "react";
|
||||
import { useThemeHooks } from '@redocly/theme/core/hooks';
|
||||
import { Link } from '@redocly/theme/components/Link/Link';
|
||||
import { PageGrid, PageGridCol, PageGridRow } from "shared/components/PageGrid/page-grid";
|
||||
|
||||
export const frontmatter = {
|
||||
seo: {
|
||||
@@ -78,14 +79,6 @@ export default function XrplOverview() {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="position-relative">
|
||||
<img
|
||||
alt="purple waves"
|
||||
src={require("../static/img/backgrounds/xrpl-overview-purple.svg")}
|
||||
className="landing-bg"
|
||||
id="xrpl-overview-purple"
|
||||
/>
|
||||
</div>
|
||||
<section className="py-26 text-center">
|
||||
<div className="col-lg-5 mx-auto text-center">
|
||||
<div className="d-flex flex-column-reverse">
|
||||
@@ -100,13 +93,6 @@ export default function XrplOverview() {
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div className="position-relative d-none-sm">
|
||||
<img
|
||||
alt="orange waves"
|
||||
src={require("../static/img/backgrounds/xrpl-overview-orange.svg")}
|
||||
id="xrpl-overview-orange"
|
||||
/>
|
||||
</div>
|
||||
<section className="container-new py-26">
|
||||
<div className="card-grid card-grid-2xN">
|
||||
<div className="col">
|
||||
@@ -133,7 +119,7 @@ export default function XrplOverview() {
|
||||
{translate("Read Technical Docs")}
|
||||
</Link>{" "}
|
||||
<a
|
||||
className="ml-4 video-external-link"
|
||||
className="ms-4 video-external-link"
|
||||
target="_blank"
|
||||
href="https://www.youtube.com/playlist?list=PLJQ55Tj1hIVZtJ_JdTvSum2qMTsedWkNi"
|
||||
>
|
||||
@@ -168,7 +154,7 @@ export default function XrplOverview() {
|
||||
{translate("Read Technical Docs")}
|
||||
</Link>{" "}
|
||||
<a
|
||||
className="ml-4 video-external-link"
|
||||
className="ms-4 video-external-link"
|
||||
target="_blank"
|
||||
href="https://www.youtube.com/playlist?list=PLJQ55Tj1hIVZtJ_JdTvSum2qMTsedWkNi"
|
||||
>
|
||||
@@ -178,9 +164,9 @@ export default function XrplOverview() {
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section className="container-new py-26">
|
||||
<div className="card-grid card-grid-2xN">
|
||||
<div className="col">
|
||||
<PageGrid className="py-26">
|
||||
<PageGridRow>
|
||||
<PageGrid.Col span={{ base: 4, lg: 6 }}>
|
||||
<div className="d-flex flex-column-reverse">
|
||||
<h2 className="h4 h2-sm mb-8">
|
||||
{translate("How the Consensus Protocol works")}
|
||||
@@ -207,25 +193,23 @@ export default function XrplOverview() {
|
||||
<p className="mb-0">
|
||||
{translate('about.index.consensus.ppart1', 'Currently, over 120 ')}
|
||||
<a href="https://livenet.xrpl.org/network/validators" target="_blank">{translate('about.index.consensus.ppart2', 'validators')}</a>
|
||||
{translate('about.index.consensus.ppart3', ' are active on the ledger, operated by universities, exchanges, businesses, and individuals. As the validator pool grows, the consensus protocol ensures decentralization of the blockchain over time.')}
|
||||
{translate('about.index.consensus.ppart3', ' are active on the ledger, operated by universities, exchanges, businesses, and individuals. As the validator pool grows, the consensus protocol ensures decentralization of the blockchain over time.')}
|
||||
</p>
|
||||
</div>
|
||||
<div className="col mb-16-sm">
|
||||
<img
|
||||
className="mw-100"
|
||||
id="validator-graphic"
|
||||
alt="(Graphic: Validators in Consensus)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section className="container-new py-26">
|
||||
<div className="col-md-6 offset-md-3 p-6-sm p-10-until-sm br-8 cta-card">
|
||||
<img
|
||||
alt="green waves"
|
||||
src={require("../static/img/backgrounds/cta-xrpl-overview-green.svg")}
|
||||
className="cta cta-bottom-right"
|
||||
/>
|
||||
</PageGrid.Col>
|
||||
<PageGrid.Col span={{ base: 4, lg: 6 }}>
|
||||
<div className="col mb-16-sm">
|
||||
<img
|
||||
className="mw-100"
|
||||
id="validator-graphic"
|
||||
alt="(Graphic: Validators in Consensus)"
|
||||
/>
|
||||
</div>
|
||||
</PageGrid.Col>
|
||||
</PageGridRow>
|
||||
</PageGrid>
|
||||
<PageGrid className="py-26">
|
||||
<PageGridRow>
|
||||
<PageGrid.Col span={{ base: 4, lg: 6 }} offset={{ lg: 3 }} className="p-6-sm p-10-until-sm br-8 cta-card">
|
||||
<div className="z-index-1 position-relative">
|
||||
<h2 className="h4 mb-10-until-sm mb-8-sm">
|
||||
{translate("A Sustainable Blockchain")}
|
||||
@@ -239,11 +223,13 @@ export default function XrplOverview() {
|
||||
{translate("Learn More")}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section className="container-new py-26">
|
||||
<div className="card-grid card-grid-2xN">
|
||||
<div className="col">
|
||||
</PageGrid.Col>
|
||||
</PageGridRow>
|
||||
</PageGrid>
|
||||
|
||||
<PageGrid className="py-26">
|
||||
<PageGridRow>
|
||||
<PageGrid.Col span={{ base: 4, lg: 6 }}>
|
||||
<div className="d-flex flex-column-reverse">
|
||||
<h4 className="h4 h2-sm mb-8">
|
||||
{translate("Building with confidence on ")}
|
||||
@@ -265,8 +251,8 @@ export default function XrplOverview() {
|
||||
<a className="btn btn-primary btn-arrow mb-10-sm" href="/about/uses">
|
||||
{translate("Explore More")}
|
||||
</a>
|
||||
</div>
|
||||
<div className="col mb-0">
|
||||
</PageGrid.Col>
|
||||
<PageGrid.Col span={{ base: 4, lg: 6 }}>
|
||||
<div className="d-flex flex-column-reverse">
|
||||
<h4 className="h4 h2-sm mb-8">
|
||||
{translate("Creating new value for long-term growth")}
|
||||
@@ -283,11 +269,11 @@ export default function XrplOverview() {
|
||||
"Significant investment in development, along with low transaction costs and energy usage, is fueling growth and opening up a wide variety of use cases at scale."
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</PageGrid.Col>
|
||||
</PageGridRow>
|
||||
</PageGrid>
|
||||
<section className="container-new py-26">
|
||||
<div className="d-flex flex-column-reverse col-xl-6 mb-lg-4 pl-0 ">
|
||||
<div className="d-flex flex-column-reverse col-xl-6 mb-lg-4 ps-0 ">
|
||||
<h2 className="h4 h2-sm">
|
||||
{translate(
|
||||
"Watch the explainer video series to learn more about the XRP Ledger"
|
||||
@@ -375,11 +361,6 @@ export default function XrplOverview() {
|
||||
</section>
|
||||
<section className="container-new py-26">
|
||||
<div className="col-md-6 offset-md-3 p-6-sm p-10-until-sm br-8 cta-card">
|
||||
<img
|
||||
alt="orange waves"
|
||||
src={require("../static/img/backgrounds/cta-xrpl-overview-orange.svg")}
|
||||
className="cta cta-bottom-right"
|
||||
/>
|
||||
<div className="z-index-1 position-relative">
|
||||
<h4 className="h4 mb-10-until-sm mb-8-sm">
|
||||
{translate("Tomorrow’s Blockchain Starts With You")}
|
||||
@@ -407,7 +388,7 @@ export default function XrplOverview() {
|
||||
</section>
|
||||
<section className="container-new py-26">
|
||||
<div
|
||||
className="col-md-6 offset-md-3 w-100 pl-0 pr-0 mini-faq"
|
||||
className="col-md-10 offset-md-1 col-lg-8 offset-lg-2 ps-0 pe-0 mini-faq"
|
||||
id="minifaq-accordion"
|
||||
>
|
||||
{faqs.map((faq, index) => (
|
||||
@@ -415,8 +396,8 @@ export default function XrplOverview() {
|
||||
<a
|
||||
href={`#heading${index + 1}`}
|
||||
className="expander collapsed"
|
||||
data-toggle="collapse"
|
||||
data-target={`#answer${index + 1}`}
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target={`#answer${index + 1}`}
|
||||
aria-expanded="false"
|
||||
aria-controls={`answer${index + 1}`}
|
||||
>
|
||||
|
||||
@@ -859,17 +859,17 @@ export default function Uses() {
|
||||
</div>
|
||||
<a
|
||||
className="btn d-block d-lg-none"
|
||||
data-toggle="modal"
|
||||
data-target="#categoryFilterModal"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#categoryFilterModal"
|
||||
>
|
||||
<span className="mr-3">
|
||||
<span className="me-3">
|
||||
<img
|
||||
src={require("../static/img/uses/usecase-filter.svg")}
|
||||
alt="Filter button"
|
||||
/>
|
||||
</span>
|
||||
{translate("Filter by Categories")}
|
||||
<span className="ml-3 total_count category_count">2</span>
|
||||
<span className="ms-3 total_count category_count">2</span>
|
||||
</a>
|
||||
{/* Start company cards */}
|
||||
<div className="row col-12 m-0 p-0 mt-4 pt-2">
|
||||
|
||||
@@ -116,400 +116,380 @@ export default function XrpOverview() {
|
||||
const totalCols = Math.max(softwallets.length, hardwallets.length) + 1;
|
||||
return (
|
||||
<div className="landing">
|
||||
<div>
|
||||
<div className="position-relative">
|
||||
<img
|
||||
alt="blue waves"
|
||||
src={require("../static/img/backgrounds/xrp-overview-blue.svg")}
|
||||
className="landing-bg"
|
||||
id="xrp-overview-blue"
|
||||
/>
|
||||
</div>
|
||||
<section className="py-26 text-center">
|
||||
<div className="col-lg-5 mx-auto text-center">
|
||||
<div className="d-flex flex-column-reverse">
|
||||
<h1 className="mb-0">
|
||||
{translate("Your Questions About XRP, Answered")}
|
||||
</h1>
|
||||
<h6 className="eyebrow mb-3">{translate("XRP Overview")}</h6>
|
||||
</div>
|
||||
<section className="py-26 text-center">
|
||||
<div className="col-lg-5 mx-auto text-center">
|
||||
<div className="d-flex flex-column-reverse">
|
||||
<h1 className="mb-0">
|
||||
{translate("Your Questions About XRP, Answered")}
|
||||
</h1>
|
||||
<h6 className="eyebrow mb-3">{translate("XRP Overview")}</h6>
|
||||
</div>
|
||||
</section>
|
||||
<section className="container-new my-20">
|
||||
<div className="card-grid card-grid-1x2">
|
||||
<div className="d-none-sm mt-lg-0">
|
||||
<ul className="page-toc no-sideline p-0 sticky-top floating-nav">
|
||||
{links.map((link) => (
|
||||
<li
|
||||
key={link.hash}
|
||||
className={`nav-item ${
|
||||
</div>
|
||||
</section>
|
||||
<section className="container-new my-20">
|
||||
<div className="card-grid card-grid-1x2">
|
||||
<div className="d-none-sm mt-lg-0">
|
||||
<ul className="page-toc no-sideline p-0 sticky-top floating-nav">
|
||||
{links.map((link) => (
|
||||
<li
|
||||
key={link.hash}
|
||||
className={`nav-item ${
|
||||
activeSection === link.hash.substring(1) ? "active" : ""
|
||||
}`}
|
||||
>
|
||||
<a
|
||||
className={`sidelinks nav-link ${
|
||||
activeSection === link.hash.substring(1) ? "active" : ""
|
||||
}`}
|
||||
href={link.hash}
|
||||
>
|
||||
{translate(link.text)}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="col mt-lg-0">
|
||||
<div className="link-section pb-26" id="about-xrp">
|
||||
<h2 className="h4 h2-sm mb-8">{translate("What Is XRP?")}</h2>
|
||||
<h5 className="longform mb-10">
|
||||
{translate(
|
||||
"about.xrp.what-is-xrp.ppart1",
|
||||
"XRP is a digital asset that’s native to the XRP Ledger—an open-source, permissionless and decentralized ",
|
||||
)}
|
||||
<a
|
||||
href="https://www.distributedagreement.com/2018/09/24/what-is-a-blockchain/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{translate("about.xrp.what-is-xrp.ppart2", "blockchain technology.")}
|
||||
</a>
|
||||
{translate("about.xrp.what-is-xrp.ppart3", " ")}
|
||||
</h5>
|
||||
|
||||
<p className="mb-6">
|
||||
{translate(
|
||||
"Created in 2012 specifically for payments, XRP can settle transactions on the ledger in 3-5 seconds. It was built to be a better Bitcoin—faster, cheaper and greener than any other digital asset."
|
||||
)}
|
||||
</p>
|
||||
<div className="overflow-x-xs">
|
||||
<table className="mb-10 landing-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<h6>{translate("Benefits")}</h6>
|
||||
</th>
|
||||
<th>
|
||||
<h6>{translate("XRP")}</h6>
|
||||
</th>
|
||||
<th>
|
||||
<h6>{translate("Bitcoin")}</h6>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{translate("Fast")}</td>
|
||||
<td>{translate("3-5 seconds to settle")}</td>
|
||||
<td>{translate("500 seconds to settle")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{translate("Low-Cost")}</td>
|
||||
<td>{translate("$0.0002/tx")}</td>
|
||||
<td>{translate("$0.50/tx")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{translate("Scalable")}</td>
|
||||
<td>{translate("1,500 tx per second")}</td>
|
||||
<td>{translate("3 tx per second")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{translate("Sustainable")}</td>
|
||||
<td>
|
||||
{translate(
|
||||
"Environmentally sustainable (negligible energy consumption)"
|
||||
)}
|
||||
</td>
|
||||
<td>
|
||||
{translate("0.3% of global energy consumption")}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<p className="mb-10">
|
||||
{translate(
|
||||
"XRP can be sent directly without needing a central intermediary, making it a convenient instrument in bridging two different currencies quickly and efficiently. It is freely exchanged on the open market and used in the real world for enabling cross-border payments and microtransactions."
|
||||
)}
|
||||
</p>
|
||||
<div className="card-grid card-grid-2xN mb-10">
|
||||
<div>
|
||||
<img
|
||||
alt="briefcase"
|
||||
className="mw-100 mb-2 invertible-img"
|
||||
src={briefcaseIcon}
|
||||
/>
|
||||
<h6 className="subhead-sm-r">
|
||||
{translate("Financial Institutions")}
|
||||
</h6>
|
||||
<p className="">
|
||||
{translate(
|
||||
"Leverage XRP as a bridge currency to facilitate faster, more affordable cross-border payments around the world."
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<img
|
||||
alt="user"
|
||||
className="mw-100 mb-2 invertible-img"
|
||||
src={userIcon}
|
||||
/>
|
||||
<h6 className="subhead-sm-r">
|
||||
{translate("Individual Consumers")}
|
||||
</h6>
|
||||
<p>
|
||||
{translate(
|
||||
"Use XRP to move different currencies around the world."
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-10 p-10 br-8 cta-card position-relative">
|
||||
<div className="z-index-1 position-relative">
|
||||
<h2 className="h4 mb-10-until-sm mb-8-sm">
|
||||
{translate(
|
||||
"The XRP Ledger is built for business."
|
||||
)}
|
||||
</h2>
|
||||
<p className="mb-10">
|
||||
{translate(
|
||||
"The only major L-1 blockchain that’s built for business and designed specifically to power finance use cases and applications at scale. Powerful enough to bootstrap a new economy, the XRP Ledger (XRPL) is fast, scalable, and sustainable."
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="py-26 link-section" id="xrp-trading">
|
||||
<h2 className="h4 h2-sm mb-8">
|
||||
{translate("How Is XRP Used in Trading?")}
|
||||
</h2>
|
||||
<h5 className="longform mb-10">
|
||||
{translate(
|
||||
"XRP is traded on more than 100 markets and exchanges worldwide."
|
||||
)}
|
||||
</h5>
|
||||
<p className="mb-6">
|
||||
{translate(
|
||||
"about.xrp.xrp-in-trading.ppart1",
|
||||
"XRP’s low transaction fees, reliability and high-speed enable traders to use the digital asset as high-speed, cost-efficient and reliable collateral across trading venues—"
|
||||
)}
|
||||
<a
|
||||
href="https://ripple.com/insights/xrp-a-preferred-base-currency-for-arbitrage-trading/"
|
||||
target="_blank"
|
||||
>
|
||||
{translate("about.xrp.xrp-in-trading.ppart2","seizing arbitrage opportunities")}
|
||||
</a>
|
||||
{translate(
|
||||
"about.xrp.xrp-in-trading.ppart3",
|
||||
", servicing margin calls and managing general trading inventory in real time."
|
||||
)}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{translate(
|
||||
"Because of the properties inherent to XRP and the ecosystem around it, traders worldwide are able to shift collateral, bridge currencies and switch from one crypto into another nearly instantly, across any exchange on the planet."
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="py-26 link-section" id="ripple">
|
||||
<h2 className="h4 h2-sm mb-8">
|
||||
{translate(
|
||||
"What Is the Relationship Between Ripple and XRP?"
|
||||
)}
|
||||
</h2>
|
||||
<h5 className="longform mb-10">
|
||||
<a href="https://ripple.com" target="_blank">
|
||||
{translate("Ripple")}
|
||||
</a>
|
||||
{translate(
|
||||
" is a technology company that makes it easier to build a high-performance, global payments business. XRP is a digital asset independent of this."
|
||||
)}
|
||||
</h5>
|
||||
|
||||
<p>
|
||||
{translate(
|
||||
"There is a finite amount of XRP. All XRP is already in existence today—no more than the original 100 billion can be created. The XRPL founders gifted 80 billion XRP, the platform’s native currency, to Ripple. To provide predictability to the XRP supply, Ripple has locked 55 billion XRP (55% of the total possible supply) into a series of escrows using the XRP Ledger itself. The XRPL's transaction processing rules, enforced by the consensus protocol, control the release of the XRP."
|
||||
)}
|
||||
</p>
|
||||
<div className="mt-10 p-10 br-8 cta-card position-relative">
|
||||
<div className="z-index-1 position-relative">
|
||||
<h3 className="h4">
|
||||
{translate("about.xrp.ripple-escrow.ppart1","As of ")}
|
||||
<span className="stat-highlight" id="ripple-escrow-as-of">
|
||||
{translate("about.xrp.ripple-escrow.ppart2","October 2024")}
|
||||
</span>
|
||||
{translate("about.xrp.ripple-escrow.ppart3"," ")}
|
||||
<br />
|
||||
<span className="d-inline-flex">
|
||||
<img
|
||||
id="xrp-mark-overview"
|
||||
className="mw-100 invertible-img me-2"
|
||||
src={require("../static/img/logos/xrp-mark.svg")}
|
||||
alt="XRP Logo Mark"
|
||||
/>
|
||||
<span
|
||||
className="numbers stat-highlight"
|
||||
id="ripple-escrow-amount"
|
||||
>
|
||||
{translate("38B")}
|
||||
</span>
|
||||
</span>
|
||||
<br />
|
||||
{translate("XRP remains in escrow")}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="link-section py-26" id="wallets">
|
||||
<h2 className="h4 h2-sm mb-8">
|
||||
{translate("What Wallets Support XRP?")}
|
||||
</h2>
|
||||
<h5 className="longform mb-10">
|
||||
{translate(
|
||||
"Digital wallets are pieces of software that allow people to send, receive, and store cryptocurrencies, including XRP. There are two types of digital wallets: hardware and software."
|
||||
)}
|
||||
</h5>
|
||||
<ul className={`nav nav-grid-lg cols-of-${totalCols}`} id="wallets">
|
||||
<li className="nav-item nav-grid-head">
|
||||
<h6 className="subhead-sm-r">{translate("Software Wallets")}</h6>
|
||||
</li>
|
||||
{softwallets.map((wallet) => (
|
||||
<li key={wallet.id} className="nav-item">
|
||||
<a
|
||||
className={`sidelinks nav-link ${
|
||||
activeSection === link.hash.substring(1) ? "active" : ""
|
||||
}`}
|
||||
href={link.hash}
|
||||
className="nav-link external-link"
|
||||
href={wallet.href}
|
||||
target="_blank"
|
||||
>
|
||||
{translate(link.text)}
|
||||
<img
|
||||
className={`mw-100 ${
|
||||
!!wallet?.imgclasses && wallet.imgclasses
|
||||
}`}
|
||||
id={wallet.id}
|
||||
alt={wallet.alt}
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
<li className="nav-item nav-grid-head">
|
||||
<h6 className="subhead-sm-r">{translate("Hardware Wallets")}</h6>
|
||||
</li>
|
||||
{hardwallets.map((wallet) => (
|
||||
<li className="nav-item" key={wallet.id}>
|
||||
<a
|
||||
className="nav-link external-link"
|
||||
href={wallet.href}
|
||||
target="_blank"
|
||||
>
|
||||
<img
|
||||
className={`mw-100 ${
|
||||
!!wallet.imgclasses && wallet.imgclasses
|
||||
}`}
|
||||
id={wallet.id}
|
||||
alt={wallet.alt}
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<p className="label-l mt-10">
|
||||
{translate(
|
||||
"Disclaimer: This information is drawn from other sources on the internet. XRPL.org does not endorse or recommend any exchanges or make any representations with respect to exchanges or the purchase or sale of digital assets more generally. It’s advisable to conduct your own due diligence before relying on any third party or third-party technology, and providers may vary significantly in their compliance, data security, and privacy practices."
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="col mt-lg-0">
|
||||
<div className="link-section pb-26" id="about-xrp">
|
||||
<h2 className="h4 h2-sm mb-8">{translate("What Is XRP?")}</h2>
|
||||
<h5 className="longform mb-10">
|
||||
{translate(
|
||||
"about.xrp.what-is-xrp.ppart1",
|
||||
"XRP is a digital asset that’s native to the XRP Ledger—an open-source, permissionless and decentralized ",
|
||||
)}
|
||||
<a
|
||||
href="https://www.distributedagreement.com/2018/09/24/what-is-a-blockchain/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{translate("about.xrp.what-is-xrp.ppart2", "blockchain technology.")}
|
||||
</a>
|
||||
{translate("about.xrp.what-is-xrp.ppart3", " ")}
|
||||
</h5>
|
||||
|
||||
<p className="mb-6">
|
||||
{translate(
|
||||
"Created in 2012 specifically for payments, XRP can settle transactions on the ledger in 3-5 seconds. It was built to be a better Bitcoin—faster, cheaper and greener than any other digital asset."
|
||||
)}
|
||||
</p>
|
||||
<div className="overflow-x-xs">
|
||||
<table className="mb-10 landing-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<h6>{translate("Benefits")}</h6>
|
||||
</th>
|
||||
<th>
|
||||
<h6>{translate("XRP")}</h6>
|
||||
</th>
|
||||
<th>
|
||||
<h6>{translate("Bitcoin")}</h6>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{translate("Fast")}</td>
|
||||
<td>{translate("3-5 seconds to settle")}</td>
|
||||
<td>{translate("500 seconds to settle")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{translate("Low-Cost")}</td>
|
||||
<td>{translate("$0.0002/tx")}</td>
|
||||
<td>{translate("$0.50/tx")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{translate("Scalable")}</td>
|
||||
<td>{translate("1,500 tx per second")}</td>
|
||||
<td>{translate("3 tx per second")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{translate("Sustainable")}</td>
|
||||
<td>
|
||||
{translate(
|
||||
"Environmentally sustainable (negligible energy consumption)"
|
||||
)}
|
||||
</td>
|
||||
<td>
|
||||
{translate("0.3% of global energy consumption")}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div className="py-26 link-section" id="exchanges">
|
||||
<h2 className="h4 h2-sm mb-8">
|
||||
{translate("What Exchanges Support XRP?")}
|
||||
</h2>
|
||||
<h5 className="longform mb-10">
|
||||
{translate(
|
||||
"Exchanges are where people trade currencies. XRP is traded on more than 100 markets and exchanges worldwide."
|
||||
)}
|
||||
</h5>
|
||||
<p className="mb-10">
|
||||
{translate(
|
||||
"There are different types of exchanges that vary depending on the type of market (spot, futures, options, swaps), and the type of security model (custodial, non-custodial)."
|
||||
)}
|
||||
</p>
|
||||
<div className="card-grid card-grid-2xN mb-10">
|
||||
<div>
|
||||
<h6 className="subhead-sm-r">{translate("Spot Exchanges")}</h6>
|
||||
<p className="mb-0">
|
||||
{translate(
|
||||
"Spot exchanges allow people to buy and sell cryptocurrencies at current (spot) market rates."
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<p className="mb-10">
|
||||
{translate(
|
||||
"XRP can be sent directly without needing a central intermediary, making it a convenient instrument in bridging two different currencies quickly and efficiently. It is freely exchanged on the open market and used in the real world for enabling cross-border payments and microtransactions."
|
||||
)}
|
||||
</p>
|
||||
<div className="card-grid card-grid-2xN mb-10">
|
||||
<div>
|
||||
<img
|
||||
alt="briefcase"
|
||||
className="mw-100 mb-2 invertible-img"
|
||||
src={briefcaseIcon}
|
||||
/>
|
||||
<h6 className="fs-4-5">
|
||||
{translate("Financial Institutions")}
|
||||
</h6>
|
||||
<p className="">
|
||||
{translate(
|
||||
"Leverage XRP as a bridge currency to facilitate faster, more affordable cross-border payments around the world."
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<img
|
||||
alt="user"
|
||||
className="mw-100 mb-2 invertible-img"
|
||||
src={userIcon}
|
||||
/>
|
||||
<h6 className="fs-4-5">
|
||||
{translate("Individual Consumers")}
|
||||
</h6>
|
||||
<p>
|
||||
{translate(
|
||||
"Use XRP to move different currencies around the world."
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h6 className="subhead-sm-r">
|
||||
{translate("Futures, Options and Swap Exchanges")}
|
||||
</h6>
|
||||
<p className="mb-0">
|
||||
{translate(
|
||||
"Futures, options and swap exchanges allow people to buy and sell standardized contracts of cryptocurrency market rates in the future."
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-10 p-10 br-8 cta-card position-relative">
|
||||
<img
|
||||
alt="magenta waves"
|
||||
src={require("../static/img/backgrounds/cta-xrp-overview-magenta.svg")}
|
||||
className="cta cta-bottom-right"
|
||||
/>
|
||||
<div className="z-index-1 position-relative">
|
||||
<h2 className="h4 mb-10-until-sm mb-8-sm">
|
||||
{translate(
|
||||
"The XRP Ledger is built for business."
|
||||
)}
|
||||
</h2>
|
||||
<p className="mb-10">
|
||||
{translate(
|
||||
"The only major L-1 blockchain that’s built for business and designed specifically to power finance use cases and applications at scale. Powerful enough to bootstrap a new economy, the XRP Ledger (XRPL) is fast, scalable, and sustainable."
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h6 className="subhead-sm-r">
|
||||
{translate("Custodial Exchanges")}
|
||||
</h6>
|
||||
<p className="mb-0">
|
||||
{translate(
|
||||
"Custodial exchanges manage a user’s private keys, and publish centralized order books of buyers and sellers."
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h6 className="subhead-sm-r">
|
||||
{translate("Non-Custodial Exchanges")}
|
||||
</h6>
|
||||
<p className="mb-0">
|
||||
{translate(
|
||||
"Non-custodial exchanges, also known as decentralized exchanges, do not manage a user’s private keys, and publish decentralized order books of buyers and sellers on a blockchain."
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="py-26 link-section" id="xrp-trading">
|
||||
<h2 className="h4 h2-sm mb-8">
|
||||
{translate("How Is XRP Used in Trading?")}
|
||||
</h2>
|
||||
<h5 className="longform mb-10">
|
||||
{translate(
|
||||
"XRP is traded on more than 100 markets and exchanges worldwide."
|
||||
)}
|
||||
</h5>
|
||||
<p className="mb-6">
|
||||
{translate(
|
||||
"about.xrp.xrp-in-trading.ppart1",
|
||||
"XRP’s low transaction fees, reliability and high-speed enable traders to use the digital asset as high-speed, cost-efficient and reliable collateral across trading venues—"
|
||||
)}
|
||||
<a
|
||||
href="https://ripple.com/insights/xrp-a-preferred-base-currency-for-arbitrage-trading/"
|
||||
target="_blank"
|
||||
>
|
||||
{translate("about.xrp.xrp-in-trading.ppart2","seizing arbitrage opportunities")}
|
||||
</a>
|
||||
{translate(
|
||||
"about.xrp.xrp-in-trading.ppart3",
|
||||
", servicing margin calls and managing general trading inventory in real time."
|
||||
)}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{translate(
|
||||
"Because of the properties inherent to XRP and the ecosystem around it, traders worldwide are able to shift collateral, bridge currencies and switch from one crypto into another nearly instantly, across any exchange on the planet."
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="py-26 link-section" id="ripple">
|
||||
<h2 className="h4 h2-sm mb-8">
|
||||
{translate(
|
||||
"What Is the Relationship Between Ripple and XRP?"
|
||||
)}
|
||||
</h2>
|
||||
<h5 className="longform mb-10">
|
||||
<a href="https://ripple.com" target="_blank">
|
||||
{translate("Ripple")}
|
||||
</a>
|
||||
{translate(
|
||||
" is a technology company that makes it easier to build a high-performance, global payments business. XRP is a digital asset independent of this."
|
||||
)}
|
||||
</h5>
|
||||
|
||||
<p>
|
||||
{translate(
|
||||
"There is a finite amount of XRP. All XRP is already in existence today—no more than the original 100 billion can be created. The XRPL founders gifted 80 billion XRP, the platform’s native currency, to Ripple. To provide predictability to the XRP supply, Ripple has locked 55 billion XRP (55% of the total possible supply) into a series of escrows using the XRP Ledger itself. The XRPL's transaction processing rules, enforced by the consensus protocol, control the release of the XRP."
|
||||
)}
|
||||
</p>
|
||||
<div className="mt-10 p-10 br-8 cta-card position-relative">
|
||||
<img
|
||||
alt="green waves"
|
||||
src={require("../static/img/backgrounds/cta-xrp-overview-green-2.svg")}
|
||||
className="landing-bg cta cta-bottom-right"
|
||||
/>
|
||||
<div className="z-index-1 position-relative">
|
||||
<h3 className="h4">
|
||||
{translate("about.xrp.ripple-escrow.ppart1","As of ")}
|
||||
<span className="stat-highlight" id="ripple-escrow-as-of">
|
||||
{translate("about.xrp.ripple-escrow.ppart2","October 2024")}
|
||||
</span>
|
||||
{translate("about.xrp.ripple-escrow.ppart3"," ")}
|
||||
<br />
|
||||
<span className="d-inline-flex">
|
||||
<img
|
||||
id="xrp-mark-overview"
|
||||
className="mw-100 invertible-img mr-2"
|
||||
src={require("../static/img/logos/xrp-mark.svg")}
|
||||
alt="XRP Logo Mark"
|
||||
/>
|
||||
<span
|
||||
className="numbers stat-highlight"
|
||||
id="ripple-escrow-amount"
|
||||
>
|
||||
{translate("38B")}
|
||||
</span>
|
||||
</span>
|
||||
<br />
|
||||
{translate("XRP remains in escrow")}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="link-section py-26" id="wallets">
|
||||
<h2 className="h4 h2-sm mb-8">
|
||||
{translate("What Wallets Support XRP?")}
|
||||
</h2>
|
||||
<h5 className="longform mb-10">
|
||||
{translate(
|
||||
"Digital wallets are pieces of software that allow people to send, receive, and store cryptocurrencies, including XRP. There are two types of digital wallets: hardware and software."
|
||||
)}
|
||||
</h5>
|
||||
<ul className={`nav nav-grid-lg cols-of-${totalCols}`} id="wallets">
|
||||
<li className="nav-item nav-grid-head">
|
||||
<h6 className="fs-4-5">{translate("Software Wallets")}</h6>
|
||||
<h6>
|
||||
{translate("Top Exchanges, according to CryptoCompare")}
|
||||
</h6>
|
||||
<ul
|
||||
className="nav nav-grid-lg cols-of-5 mb-10"
|
||||
id="top-exchanges"
|
||||
>
|
||||
{exchanges.map((exch, i) => (
|
||||
<li className="nav-item" key={exch.id}>
|
||||
<a
|
||||
className="nav-link external-link"
|
||||
href={exch.href}
|
||||
target="_blank"
|
||||
>
|
||||
<span className="longform me-3">{i+1}</span>
|
||||
<img className="mw-100" id={exch.id} alt={exch.alt} />
|
||||
</a>
|
||||
</li>
|
||||
{softwallets.map((wallet) => (
|
||||
<li key={wallet.id} className="nav-item">
|
||||
<a
|
||||
className="nav-link external-link"
|
||||
href={wallet.href}
|
||||
target="_blank"
|
||||
>
|
||||
<img
|
||||
className={`mw-100 ${
|
||||
!!wallet?.imgclasses && wallet.imgclasses
|
||||
}`}
|
||||
id={wallet.id}
|
||||
alt={wallet.alt}
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
<li className="nav-item nav-grid-head">
|
||||
<h6 className="fs-4-5">{translate("Hardware Wallets")}</h6>
|
||||
</li>
|
||||
{hardwallets.map((wallet) => (
|
||||
<li className="nav-item" key={wallet.id}>
|
||||
<a
|
||||
className="nav-link external-link"
|
||||
href={wallet.href}
|
||||
target="_blank"
|
||||
>
|
||||
<img
|
||||
className={`mw-100 ${
|
||||
!!wallet.imgclasses && wallet.imgclasses
|
||||
}`}
|
||||
id={wallet.id}
|
||||
alt={wallet.alt}
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<p className="fs-3 mt-10">
|
||||
{translate(
|
||||
"Disclaimer: This information is drawn from other sources on the internet. XRPL.org does not endorse or recommend any exchanges or make any representations with respect to exchanges or the purchase or sale of digital assets more generally. It’s advisable to conduct your own due diligence before relying on any third party or third-party technology, and providers may vary significantly in their compliance, data security, and privacy practices."
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="py-26 link-section" id="exchanges">
|
||||
<h2 className="h4 h2-sm mb-8">
|
||||
{translate("What Exchanges Support XRP?")}
|
||||
</h2>
|
||||
<h5 className="longform mb-10">
|
||||
{translate(
|
||||
"Exchanges are where people trade currencies. XRP is traded on more than 100 markets and exchanges worldwide."
|
||||
)}
|
||||
</h5>
|
||||
<p className="mb-10">
|
||||
{translate(
|
||||
"There are different types of exchanges that vary depending on the type of market (spot, futures, options, swaps), and the type of security model (custodial, non-custodial)."
|
||||
)}
|
||||
</p>
|
||||
<div className="card-grid card-grid-2xN mb-10">
|
||||
<div>
|
||||
<h6 className="fs-4-5">{translate("Spot Exchanges")}</h6>
|
||||
<p className="mb-0">
|
||||
{translate(
|
||||
"Spot exchanges allow people to buy and sell cryptocurrencies at current (spot) market rates."
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h6 className="fs-4-5">
|
||||
{translate("Futures, Options and Swap Exchanges")}
|
||||
</h6>
|
||||
<p className="mb-0">
|
||||
{translate(
|
||||
"Futures, options and swap exchanges allow people to buy and sell standardized contracts of cryptocurrency market rates in the future."
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h6 className="fs-4-5">
|
||||
{translate("Custodial Exchanges")}
|
||||
</h6>
|
||||
<p className="mb-0">
|
||||
{translate(
|
||||
"Custodial exchanges manage a user’s private keys, and publish centralized order books of buyers and sellers."
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h6 className="fs-4-5">
|
||||
{translate("Non-Custodial Exchanges")}
|
||||
</h6>
|
||||
<p className="mb-0">
|
||||
{translate(
|
||||
"Non-custodial exchanges, also known as decentralized exchanges, do not manage a user’s private keys, and publish decentralized order books of buyers and sellers on a blockchain."
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<h6>
|
||||
{translate("Top Exchanges, according to CryptoCompare")}
|
||||
</h6>
|
||||
<ul
|
||||
className="nav nav-grid-lg cols-of-5 mb-10"
|
||||
id="top-exchanges"
|
||||
>
|
||||
{exchanges.map((exch, i) => (
|
||||
<li className="nav-item" key={exch.id}>
|
||||
<a
|
||||
className="nav-link external-link"
|
||||
href={exch.href}
|
||||
target="_blank"
|
||||
>
|
||||
<span className="longform mr-3">{i+1}</span>
|
||||
<img className="mw-100" id={exch.id} alt={exch.alt} />
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<p className="fs-3 mt-10 mb-0">
|
||||
{translate(
|
||||
"Disclaimer: This information is drawn from other sources on the internet. XRPL.org does not endorse or recommend any exchanges or make any representations with respect to exchanges or the purchase or sale of digital assets more generally. It’s advisable to conduct your own due diligence before relying on any third party or third-party technology, and providers may vary significantly in their compliance, data security, and privacy practices."
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</ul>
|
||||
<p className="label-l mt-10 mb-0">
|
||||
{translate(
|
||||
"Disclaimer: This information is drawn from other sources on the internet. XRPL.org does not endorse or recommend any exchanges or make any representations with respect to exchanges or the purchase or sale of digital assets more generally. It’s advisable to conduct your own due diligence before relying on any third party or third-party technology, and providers may vary significantly in their compliance, data security, and privacy practices."
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -57,13 +57,6 @@ export default function Index() {
|
||||
return (
|
||||
<div className="landing dev-blog">
|
||||
<div className="justify-content-center align-items-lg-center">
|
||||
<div className="position-relative d-none-sm">
|
||||
<img
|
||||
alt="background purple waves"
|
||||
src={require("../static/img/backgrounds/home-purple.svg")}
|
||||
id="blog-purple"
|
||||
/>
|
||||
</div>
|
||||
<section className="py-lg-5 text-center mt-lg-5">
|
||||
<div className="mx-auto text-center col-lg-5">
|
||||
<div className="d-flex flex-column">
|
||||
|
||||
@@ -3,8 +3,8 @@ import { useThemeHooks } from '@redocly/theme/core/hooks';
|
||||
|
||||
export const frontmatter = {
|
||||
seo: {
|
||||
title: 'Ambassadors',
|
||||
description: "The XRPL Campus Ambassador program connects and empowers student champions of the XRPL.",
|
||||
title: 'Ambassadors',
|
||||
description: "The XRPL Campus Ambassador program connects and empowers student champions of the XRPL.",
|
||||
}
|
||||
};
|
||||
|
||||
@@ -17,409 +17,403 @@ export default function Ambassadors() {
|
||||
const { translate } = useTranslate();
|
||||
|
||||
return (
|
||||
<div className="landing page-ambassadors">
|
||||
<div>
|
||||
<div className="position-relative d-none-sm">
|
||||
<img alt="background purple waves" src={require("../static/img/backgrounds/ambassador-purple.svg")} className="position-absolute" style={{top: 0, right: 0}} />
|
||||
</div>
|
||||
<div className="landing page-ambassadors">
|
||||
<section className="container-new py-26 text-lg-center">
|
||||
{/* For translater: This section could change dynamically based on the time of year */}
|
||||
<div className="p-0 col-lg-8 mx-lg-auto">
|
||||
<div className="d-flex flex-column-reverse">
|
||||
<h1 className="mb-0">{translate("Become an XRP Ledger Campus Ambassador")}</h1>
|
||||
<h6 className="eyebrow mb-3">{translate("Join the Student Cohort")}</h6>
|
||||
</div>
|
||||
<p className="mt-3 pt-3 col-lg-8 mx-lg-auto p-0">{translate("This fall, the ")} <b>{translate("XRPL Student Builder Residency ")}</b> {translate("offers top technical students a 3-week online program (Oct 21 - Nov 13) to develop XRPL projects with expert mentorship. Apply by Oct 14, 2024")}</p>
|
||||
<p className=" col-lg-8 mx-lg-auto p-0">{translate("This program will run from October 21 - November 13 and will be conducted entirely online. ")}</p>
|
||||
<p className="pb-3 col-lg-8 mx-lg-auto p-0"><b>{translate("Applications due October 14, 2024")}</b>{translate(" @ 11:59pm PDT")}</p>
|
||||
<button className="btn btn-primary btn-arrow-out" onClick={() => window.open('https://share.hsforms.com/1k47bfuX2SL2DKZtZoJzArg4vgrs', "_blank")} >{translate("Apply for Fall 2024")}</button>
|
||||
{/* For translater: This section could change dynamically based on the time of year */}
|
||||
<div className="p-0 col-lg-8 mx-lg-auto">
|
||||
<div className="d-flex flex-column-reverse">
|
||||
<h1 className="mb-0">{translate("Become an XRP Ledger Campus Ambassador")}</h1>
|
||||
<h6 className="eyebrow mb-3">{translate("Join the Student Cohort")}</h6>
|
||||
</div>
|
||||
<p className="mt-3 pt-3 col-lg-8 mx-lg-auto p-0">{translate("This fall, the ")} <b>{translate("XRPL Student Builder Residency ")}</b> {translate("offers top technical students a 3-week online program (Oct 21 - Nov 13) to develop XRPL projects with expert mentorship. Apply by Oct 14, 2024")}</p>
|
||||
<p className=" col-lg-8 mx-lg-auto p-0">{translate("This program will run from October 21 - November 13 and will be conducted entirely online. ")}</p>
|
||||
<p className="pb-3 col-lg-8 mx-lg-auto p-0"><b>{translate("Applications due October 14, 2024")}</b>{translate(" @ 11:59pm PDT")}</p>
|
||||
<button className="btn btn-primary btn-arrow-out" onClick={() => window.open('https://share.hsforms.com/1k47bfuX2SL2DKZtZoJzArg4vgrs', "_blank")} >{translate("Apply for Fall 2024")}</button>
|
||||
</div>
|
||||
</section>
|
||||
{/* Current Students */}
|
||||
<section className="container-new py-26">
|
||||
<div className="d-flex flex-column flex-lg-row align-items-lg-center">
|
||||
<div className="order-lg-2 mx-lg-4 mb-4 pb-3 mb-lg-0 pb-lg-0 col-lg-6 px-0 pr-lg-5">
|
||||
<div className="d-flex flex-column-reverse p-lg-3">
|
||||
<h3 className="h4 h2-sm">{translate("XRPL Campus Ambassadors")}</h3>
|
||||
<h6 className="eyebrow mb-3">{translate("Empowering Students")}</h6>
|
||||
</div>
|
||||
<p className="p-lg-3 mb-2 longform">{translate("The XRPL Campus Ambassador program aims to elevate the impact of college students who are passionate about blockchain technology.")}</p>
|
||||
<div className="d-none d-lg-block p-lg-3">
|
||||
<button className="btn btn-primary btn-arrow-out" onClick={() => window.open('https://share.hsforms.com/1k47bfuX2SL2DKZtZoJzArg4vgrs', "_blank")} >{translate("Apply for Fall 2024")}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="order-lg-1 col-lg-6 px-0 mr-lg-4">
|
||||
<div className="row m-0">
|
||||
<img alt="Person speaking and person taking photo" src={require("../static/img/ambassadors/developer-hero@2x.png")} className="w-100" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-lg-none order-3 mt-4 pt-3 p-lg-3">
|
||||
<button className="btn btn-primary btn-arrow-out" onClick={() => window.open('https://share.hsforms.com/1k47bfuX2SL2DKZtZoJzArg4vgrs', "_blank")} >{translate("Apply for Fall 2024")}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-flex flex-column flex-lg-row align-items-lg-center">
|
||||
<div className="order-lg-2 mx-lg-4 mb-4 pb-3 mb-lg-0 pb-lg-0 col-lg-6 px-0 pr-lg-5">
|
||||
<div className="d-flex flex-column-reverse p-lg-3">
|
||||
<h3 className="h4 h2-sm">{translate("XRPL Campus Ambassadors")}</h3>
|
||||
<h6 className="eyebrow mb-3">{translate("Empowering Students")}</h6>
|
||||
</div>
|
||||
<p className="p-lg-3 mb-2 longform">{translate("The XRPL Campus Ambassador program aims to elevate the impact of college students who are passionate about blockchain technology.")}</p>
|
||||
<div className="d-none d-lg-block p-lg-3">
|
||||
<button className="btn btn-primary btn-arrow-out" onClick={() => window.open('https://share.hsforms.com/1k47bfuX2SL2DKZtZoJzArg4vgrs', "_blank")} >{translate("Apply for Fall 2024")}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="order-lg-1 col-lg-6 px-0 mr-lg-4">
|
||||
<div className="row m-0">
|
||||
<img alt="Person speaking and person taking photo" src={require("../static/img/ambassadors/developer-hero@2x.png")} className="w-100" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-lg-none order-3 mt-4 pt-3 p-lg-3">
|
||||
<button className="btn btn-primary btn-arrow-out" onClick={() => window.open('https://share.hsforms.com/1k47bfuX2SL2DKZtZoJzArg4vgrs', "_blank")} >{translate("Apply for Fall 2024")}</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{/* Benefits */}
|
||||
<section className="container-new py-26">
|
||||
{/* flex. Col for mobile. Row for large. on large align content to the center */}
|
||||
<div className="d-flex flex-column flex-lg-row align-items-lg-center">
|
||||
<div className="order-1 mb-4 pb-3 mb-lg-0 pb-lg-0 col-lg-6 px-0">
|
||||
<div className="d-flex flex-column-reverse p-lg-3">
|
||||
<h3 className="h4 h2-sm">{translate("Why become an XRPL Campus Ambassador?")}</h3>
|
||||
<h6 className="eyebrow mb-3">{translate("Benefits")}</h6>
|
||||
</div>
|
||||
<p className="p-lg-3 mb-2 longform">{translate("Join a global cohort of students empowering others to build on the XRPL.")}</p>
|
||||
</div>
|
||||
<div className="order-2 col-lg-6 px-0 mr-lg-5">
|
||||
<div className="row align-items-center m-0" id="benefits-list">
|
||||
{/* benefitslist */}
|
||||
<div className="col-12 col-lg-6 p-0 pr-lg-4">
|
||||
<div className="px-lg-3 pb-3">
|
||||
<img alt="Smiley face" id="benefits-01" className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3">
|
||||
<h6 className="mb-3">{translate("Exclusive Opportunities")}</h6>
|
||||
<p>{translate("Get access and invitations to Ambassador-only events and opportunities")}</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* Hide on large */}
|
||||
<div className="px-lg-3 pb-3 d-lg-none ">
|
||||
<img alt="Book" id="benefits-02" className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3">
|
||||
<h6 className="mb-3">{translate("Education")}</h6>
|
||||
<p>{translate("Tutorials and workshops from leading XRPL and blockchain developers")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-lg-3 pb-3">
|
||||
<img alt="Gift" id="benefits-03" className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3">
|
||||
<h6 className="mb-3">{translate("Swag")}</h6>
|
||||
<p>{translate("New XRPL swag for Ambassadors and swag to share with other students")}</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* Hide on large */}
|
||||
<div className="px-lg-3 pb-3 d-lg-none">
|
||||
<img alt="Medallion" id="benefits-04" className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3">
|
||||
<h6 className="mb-3">{translate("Mentorship")}</h6>
|
||||
<p>{translate("Meet with and learn from influential builders and leaders across the XRPL community")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-lg-3 pb-3">
|
||||
<img alt="Up Arrow" id="benefits-05" className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3 pb-lg-0">
|
||||
<h6 className="mb-3">{translate("Career Acceleration")}</h6>
|
||||
<p className="pb-lg-0">{translate("Gain hands-on experience building communities and grow your professional network in the blockchain industry")}</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* Hide on large */}
|
||||
<div className="px-lg-3 pb-3 d-lg-none">
|
||||
<img alt="Dollar Sign" id="benefits-06" className="pl-lg-3" />
|
||||
<div className="pb-lg-0">
|
||||
<h6 className="mb-3">{translate("Stipend")}</h6>
|
||||
<p className="pb-lg-0">{translate("Receive a stipend to fund your ideas and initiatives that fuel XRPL growth in your community")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* end col 1 */}
|
||||
{/* Show on large */}
|
||||
<div className="col-12 col-lg-6 p-0 pl-lg-4 d-none d-lg-block">
|
||||
<div className="px-lg-3 pb-3 pt-5 mt-5">
|
||||
<img alt="Book" id="benefits-02" className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3">
|
||||
<h6 className="mb-3">{translate("Education")}</h6>
|
||||
<p>{translate("Tutorials and workshops from leading XRPL and blockchain developers")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-lg-3 pb-3 ">
|
||||
<img alt="Medallion" id="benefits-04" className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3">
|
||||
<h6 className="mb-3">{translate("Mentorship")}</h6>
|
||||
<p>{translate("Meet with and learn from influential builders and leaders across the XRPL community")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-lg-3 pb-3">
|
||||
<img alt="Dollar Sign" id="benefits-06" className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3 pb-lg-0">
|
||||
<h6 className="mb-3">{translate("Stipend")}</h6>
|
||||
<p className="pb-lg-0">{translate("Receive a stipend to fund your ideas and initiatives that fuel XRPL growth in your community")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* end col 2 */}
|
||||
</div>
|
||||
</div>
|
||||
{/* flex. Col for mobile. Row for large. on large align content to the center */}
|
||||
<div className="d-flex flex-column flex-lg-row align-items-lg-center">
|
||||
<div className="order-1 mb-4 pb-3 mb-lg-0 pb-lg-0 col-lg-6 px-0">
|
||||
<div className="d-flex flex-column-reverse p-lg-3">
|
||||
<h3 className="h4 h2-sm">{translate("Why become an XRPL Campus Ambassador?")}</h3>
|
||||
<h6 className="eyebrow mb-3">{translate("Benefits")}</h6>
|
||||
</div>
|
||||
<p className="p-lg-3 mb-2 longform">{translate("Join a global cohort of students empowering others to build on the XRPL.")}</p>
|
||||
</div>
|
||||
<div className="order-2 col-lg-6 px-0 mr-lg-5">
|
||||
<div className="row align-items-center m-0" id="benefits-list">
|
||||
{/* benefitslist */}
|
||||
<div className="col-12 col-lg-6 p-0 pr-lg-4">
|
||||
<div className="px-lg-3 pb-3">
|
||||
<img alt="Smiley face" id="benefits-01" className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3">
|
||||
<h6 className="mb-3">{translate("Exclusive Opportunities")}</h6>
|
||||
<p>{translate("Get access and invitations to Ambassador-only events and opportunities")}</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* Hide on large */}
|
||||
<div className="px-lg-3 pb-3 d-lg-none ">
|
||||
<img alt="Book" id="benefits-02" className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3">
|
||||
<h6 className="mb-3">{translate("Education")}</h6>
|
||||
<p>{translate("Tutorials and workshops from leading XRPL and blockchain developers")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-lg-3 pb-3">
|
||||
<img alt="Gift" id="benefits-03" className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3">
|
||||
<h6 className="mb-3">{translate("Swag")}</h6>
|
||||
<p>{translate("New XRPL swag for Ambassadors and swag to share with other students")}</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* Hide on large */}
|
||||
<div className="px-lg-3 pb-3 d-lg-none">
|
||||
<img alt="Medallion" id="benefits-04" className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3">
|
||||
<h6 className="mb-3">{translate("Mentorship")}</h6>
|
||||
<p>{translate("Meet with and learn from influential builders and leaders across the XRPL community")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-lg-3 pb-3">
|
||||
<img alt="Up Arrow" id="benefits-05" className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3 pb-lg-0">
|
||||
<h6 className="mb-3">{translate("Career Acceleration")}</h6>
|
||||
<p className="pb-lg-0">{translate("Gain hands-on experience building communities and grow your professional network in the blockchain industry")}</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* Hide on large */}
|
||||
<div className="px-lg-3 pb-3 d-lg-none">
|
||||
<img alt="Dollar Sign" id="benefits-06" className="pl-lg-3" />
|
||||
<div className="pb-lg-0">
|
||||
<h6 className="mb-3">{translate("Stipend")}</h6>
|
||||
<p className="pb-lg-0">{translate("Receive a stipend to fund your ideas and initiatives that fuel XRPL growth in your community")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* end col 1 */}
|
||||
{/* Show on large */}
|
||||
<div className="col-12 col-lg-6 p-0 pl-lg-4 d-none d-lg-block">
|
||||
<div className="px-lg-3 pb-3 pt-5 mt-5">
|
||||
<img alt="Book" id="benefits-02" className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3">
|
||||
<h6 className="mb-3">{translate("Education")}</h6>
|
||||
<p>{translate("Tutorials and workshops from leading XRPL and blockchain developers")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-lg-3 pb-3 ">
|
||||
<img alt="Medallion" id="benefits-04" className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3">
|
||||
<h6 className="mb-3">{translate("Mentorship")}</h6>
|
||||
<p>{translate("Meet with and learn from influential builders and leaders across the XRPL community")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-lg-3 pb-3">
|
||||
<img alt="Dollar Sign" id="benefits-06" className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3 pb-lg-0">
|
||||
<h6 className="mb-3">{translate("Stipend")}</h6>
|
||||
<p className="pb-lg-0">{translate("Receive a stipend to fund your ideas and initiatives that fuel XRPL growth in your community")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* end col 2 */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{/* Eligibility */}
|
||||
<section className="container-new py-26">
|
||||
{/* flex. Col for mobile. Row for large. on large align content to the center */}
|
||||
<div className="d-flex flex-column flex-lg-row align-items-lg-center mr-lg-4">
|
||||
<div className="order-1 order-lg-2 mb-4 pb-3 mb-lg-0 pb-lg-0 col-lg-6 px-0 mr-lg-5">
|
||||
<div className="d-flex flex-column-reverse p-lg-3">
|
||||
<h3 className="h4 h2-sm">{translate("Should You Apply?")}</h3>
|
||||
<h6 className="eyebrow mb-3">{translate("Eligibility for XRPL Campus Ambassadors")}</h6>
|
||||
</div>
|
||||
<p className="p-lg-3 mb-2 longform">{translate("Students currently enrolled in an undergraduate or postgraduate program at an accredited college or university are eligible to apply.")}</p>
|
||||
</div>
|
||||
<div className="order-2 order-lg-1 col-lg-6 px-0">
|
||||
<div className="row align-items-center m-0" id="eligibility-list">
|
||||
<div className="col-12 col-lg-6 p-0 pr-lg-4">
|
||||
<div className="px-lg-3 pb-3">
|
||||
<img alt="Calendar" id="eligibility-01" className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3">
|
||||
<h6 className="mb-3">{translate("A Leader")}</h6>
|
||||
<p>{translate("Interested in leading meetups and workshops for your local campus community")}</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* Hide on large */}
|
||||
<div className="px-lg-3 pb-3 d-lg-none ">
|
||||
<img alt="Book" id="eligibility-02" className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3">
|
||||
<h6 className="mb-3">{translate("Active")}</h6>
|
||||
<p>{translate("An active participant in the XRPL community or interested in blockchain and crypto technologies")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-lg-3 pb-3">
|
||||
<img alt="CPU" id="eligibility-03" className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3">
|
||||
<h6 className="mb-3">{translate("Curious")}</h6>
|
||||
<p>{translate("Eager to learn more about technical blockchain topics and the XRPL")}</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* Hide on large */}
|
||||
<div className="px-lg-3 pb-3 d-lg-none">
|
||||
<img alt="Quote Bubble" id="eligibility-04" className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3 pb-lg-0">
|
||||
<h6 className="mb-3">{translate("Passionate")}</h6>
|
||||
<p>{translate("Passionate about increasing XRPL education and awareness through events, content, and classroom engagement")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-lg-3 pb-3">
|
||||
<img alt="People" id="eligibility-05" className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3 pb-lg-0">
|
||||
<h6 className="mb-3">{translate("Creative")}</h6>
|
||||
<p className="pb-lg-0 mb-0">{translate("Ability to think outside the box and have an impact in the XRPL student community")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* end col 1 */}
|
||||
{/* Show on large */}
|
||||
<div className="col-12 col-lg-6 p-0 pl-lg-4 d-none d-lg-block">
|
||||
<div className="px-lg-3 pb-3 ">
|
||||
<img alt="Book" id="eligibility-02" className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3">
|
||||
<h6 className="mb-3">{translate("Active")}</h6>
|
||||
<p>{translate("An active participant in the XRPL community or interested in blockchain and crypto technologies")} </p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-lg-3 pb-3 ">
|
||||
<img alt="Quote Bubble" id="eligibility-04" className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3 pb-lg-0">
|
||||
<h6 className="mb-3">{translate("Passionate")}</h6>
|
||||
<p> {translate("Passionate about increasing XRPL education and awareness through events, content, and classroom engagement")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* end col 2 */}
|
||||
</div>
|
||||
</div>
|
||||
{/* flex. Col for mobile. Row for large. on large align content to the center */}
|
||||
<div className="d-flex flex-column flex-lg-row align-items-lg-center mr-lg-4">
|
||||
<div className="order-1 order-lg-2 mb-4 pb-3 mb-lg-0 pb-lg-0 col-lg-6 px-0 mr-lg-5">
|
||||
<div className="d-flex flex-column-reverse p-lg-3">
|
||||
<h3 className="h4 h2-sm">{translate("Should You Apply?")}</h3>
|
||||
<h6 className="eyebrow mb-3">{translate("Eligibility for XRPL Campus Ambassadors")}</h6>
|
||||
</div>
|
||||
<p className="p-lg-3 mb-2 longform">{translate("Students currently enrolled in an undergraduate or postgraduate program at an accredited college or university are eligible to apply.")}</p>
|
||||
</div>
|
||||
<div className="order-2 order-lg-1 col-lg-6 px-0">
|
||||
<div className="row align-items-center m-0" id="eligibility-list">
|
||||
<div className="col-12 col-lg-6 p-0 pr-lg-4">
|
||||
<div className="px-lg-3 pb-3">
|
||||
<img alt="Calendar" id="eligibility-01" className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3">
|
||||
<h6 className="mb-3">{translate("A Leader")}</h6>
|
||||
<p>{translate("Interested in leading meetups and workshops for your local campus community")}</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* Hide on large */}
|
||||
<div className="px-lg-3 pb-3 d-lg-none ">
|
||||
<img alt="Book" id="eligibility-02" className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3">
|
||||
<h6 className="mb-3">{translate("Active")}</h6>
|
||||
<p>{translate("An active participant in the XRPL community or interested in blockchain and crypto technologies")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-lg-3 pb-3">
|
||||
<img alt="CPU" id="eligibility-03" className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3">
|
||||
<h6 className="mb-3">{translate("Curious")}</h6>
|
||||
<p>{translate("Eager to learn more about technical blockchain topics and the XRPL")}</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* Hide on large */}
|
||||
<div className="px-lg-3 pb-3 d-lg-none">
|
||||
<img alt="Quote Bubble" id="eligibility-04" className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3 pb-lg-0">
|
||||
<h6 className="mb-3">{translate("Passionate")}</h6>
|
||||
<p>{translate("Passionate about increasing XRPL education and awareness through events, content, and classroom engagement")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-lg-3 pb-3">
|
||||
<img alt="People" id="eligibility-05" className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3 pb-lg-0">
|
||||
<h6 className="mb-3">{translate("Creative")}</h6>
|
||||
<p className="pb-lg-0 mb-0">{translate("Ability to think outside the box and have an impact in the XRPL student community")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* end col 1 */}
|
||||
{/* Show on large */}
|
||||
<div className="col-12 col-lg-6 p-0 pl-lg-4 d-none d-lg-block">
|
||||
<div className="px-lg-3 pb-3 ">
|
||||
<img alt="Book" id="eligibility-02" className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3">
|
||||
<h6 className="mb-3">{translate("Active")}</h6>
|
||||
<p>{translate("An active participant in the XRPL community or interested in blockchain and crypto technologies")} </p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-lg-3 pb-3 ">
|
||||
<img alt="Quote Bubble" id="eligibility-04" className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3 pb-lg-0">
|
||||
<h6 className="mb-3">{translate("Passionate")}</h6>
|
||||
<p> {translate("Passionate about increasing XRPL education and awareness through events, content, and classroom engagement")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* end col 2 */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{/* Current Students */}
|
||||
<section className="container-new py-26">
|
||||
{/* Quotes */}
|
||||
<div id="carouselSlidesOnly" className="carousel slide col-lg-10 mx-auto px-0" data-ride="carousel">
|
||||
<div className="carousel-inner">
|
||||
<div className="carousel-item active">
|
||||
<div className="p-0">
|
||||
<div className="mb-4 p-lg-3">
|
||||
<img alt="I have learned so much through creating programs and connecting with the XRPL community. Im truly grateful for everyone's support along the way and for the opportunity to gain so much knowledge from this expierence" src={require("../static/img/ambassadors/quote1-small.svg")} className="h-100 d-lg-none mb-4" />
|
||||
<img alt="I have learned so much through creating programs and connecting with the XRPL community. Im truly grateful for everyone's support along the way and for the opportunity to gain so much knowledge from this expierence" src={require("../static/img/ambassadors/quote1.svg")} className="h-100 d-none d-lg-block" />
|
||||
<div className="p-0 col-lg-7 mx-lg-auto">
|
||||
<p className="p-lg-3 mb-2"><strong>Derrick N.</strong><br />
|
||||
Toronto Metropolitan University<br />
|
||||
Spring 2023 XRPL Campus Ambassador</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="carousel-item mb-20">
|
||||
<div className="p-0">
|
||||
<div className="mb-4 p-lg-3">
|
||||
<img alt="The XRPL Campus Ambassador program really helped broaden my view of the blockchain industry with their learning resource and virtual community. Being an ambassador allowed me to meet industry professionals and likeminded peers which have given me invaluable experiences and insights." src={require("../static/img/ambassadors/quote2-small.svg")} className="h-150 d-lg-none mb-4" />
|
||||
<img alt="The XRPL Campus Ambassador program really helped broaden my view of the blockchain industry with their learning resource and virtual community. Being an ambassador allowed me to meet industry professionals and likeminded peers which have given me invaluable experiences and insights." src={require("../static/img/ambassadors/quote2.svg")} className="h-100 d-none d-lg-block" />
|
||||
<div className="p-0 col-lg-7 mx-lg-auto">
|
||||
<p className="p-lg-3 mb-2"><strong>Sally Z.</strong><br />
|
||||
Toronto Metropolitan University<br />
|
||||
Spring 2023 XRPL Campus Ambassador</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="carousel-item mb-40">
|
||||
<div className="p-0">
|
||||
<div className="mb-4 p-lg-3">
|
||||
<img alt="Ive had the pleasure over the course of this program to speak with amazing individuals, I encourage you all to reach out to other people in this program and make as many connections as you can. You will quickly find out that by speaking with other people in this cohort you can learn just about anything if you ask the right people." src={require("../static/img/ambassadors/quote3-small.svg")} className="h-150 d-lg-none mb-4" />
|
||||
<img alt="Ive had the pleasure over the course of this program to speak with amazing individuals, I encourage you all to reach out to other people in this program and make as many connections as you can. You will quickly find out that by speaking with other people in this cohort you can learn just about anything if you ask the right people." src={require("../static/img/ambassadors/quote3.svg")} className="h-100 d-none d-lg-block" />
|
||||
<div className="p-0 col-lg-7 mx-lg-auto">
|
||||
<p className="p-lg-3 mb-2"><strong>Nick D.</strong><br />
|
||||
Miami University<br />
|
||||
Spring 2023 XRPL Campus Ambassador</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* Quotes */}
|
||||
<div id="carouselSlidesOnly" className="carousel slide col-lg-10 mx-auto px-0" data-ride="carousel">
|
||||
<div className="carousel-inner">
|
||||
<div className="carousel-item active">
|
||||
<div className="p-0">
|
||||
<div className="mb-4 p-lg-3">
|
||||
<img alt="I have learned so much through creating programs and connecting with the XRPL community. Im truly grateful for everyone's support along the way and for the opportunity to gain so much knowledge from this expierence" src={require("../static/img/ambassadors/quote1-small.svg")} className="h-100 d-lg-none mb-4" />
|
||||
<img alt="I have learned so much through creating programs and connecting with the XRPL community. Im truly grateful for everyone's support along the way and for the opportunity to gain so much knowledge from this expierence" src={require("../static/img/ambassadors/quote1.svg")} className="h-100 d-none d-lg-block" />
|
||||
<div className="p-0 col-lg-7 mx-lg-auto">
|
||||
<p className="p-lg-3 mb-2"><strong>Derrick N.</strong><br />
|
||||
Toronto Metropolitan University<br />
|
||||
Spring 2023 XRPL Campus Ambassador</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="carousel-item mb-20">
|
||||
<div className="p-0">
|
||||
<div className="mb-4 p-lg-3">
|
||||
<img alt="The XRPL Campus Ambassador program really helped broaden my view of the blockchain industry with their learning resource and virtual community. Being an ambassador allowed me to meet industry professionals and likeminded peers which have given me invaluable experiences and insights." src={require("../static/img/ambassadors/quote2-small.svg")} className="h-150 d-lg-none mb-4" />
|
||||
<img alt="The XRPL Campus Ambassador program really helped broaden my view of the blockchain industry with their learning resource and virtual community. Being an ambassador allowed me to meet industry professionals and likeminded peers which have given me invaluable experiences and insights." src={require("../static/img/ambassadors/quote2.svg")} className="h-100 d-none d-lg-block" />
|
||||
<div className="p-0 col-lg-7 mx-lg-auto">
|
||||
<p className="p-lg-3 mb-2"><strong>Sally Z.</strong><br />
|
||||
Toronto Metropolitan University<br />
|
||||
Spring 2023 XRPL Campus Ambassador</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="carousel-item mb-40">
|
||||
<div className="p-0">
|
||||
<div className="mb-4 p-lg-3">
|
||||
<img alt="Ive had the pleasure over the course of this program to speak with amazing individuals, I encourage you all to reach out to other people in this program and make as many connections as you can. You will quickly find out that by speaking with other people in this cohort you can learn just about anything if you ask the right people." src={require("../static/img/ambassadors/quote3-small.svg")} className="h-150 d-lg-none mb-4" />
|
||||
<img alt="Ive had the pleasure over the course of this program to speak with amazing individuals, I encourage you all to reach out to other people in this program and make as many connections as you can. You will quickly find out that by speaking with other people in this cohort you can learn just about anything if you ask the right people." src={require("../static/img/ambassadors/quote3.svg")} className="h-100 d-none d-lg-block" />
|
||||
<div className="p-0 col-lg-7 mx-lg-auto">
|
||||
<p className="p-lg-3 mb-2"><strong>Nick D.</strong><br />
|
||||
Miami University<br />
|
||||
Spring 2023 XRPL Campus Ambassador</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{/* How it Works */}
|
||||
<section className="container-new py-26">
|
||||
{/* flex. Col for mobile. Row for large. on large align content to the center */}
|
||||
<div className="d-flex flex-column flex-lg-row align-items-lg-center">
|
||||
<div className="order-1 mr-lg-4 mb-4 pb-3 mb-lg-0 pb-lg-0 col-lg-6 px-0">
|
||||
<div className="d-flex flex-column-reverse p-lg-3">
|
||||
<h3 className="h4 h2-sm">{translate("Process to become a Campus Ambassador")}</h3>
|
||||
<h6 className="eyebrow mb-3">{translate("How it Works")}</h6>
|
||||
</div>
|
||||
<p className="p-lg-3 mb-2 longform">{translate("Apply now to become an XRPL Campus Ambassador.")}</p>
|
||||
<div className="d-none d-lg-block p-lg-3">
|
||||
<button className="btn btn-primary btn-arrow-out" onClick={() => window.open('https://share.hsforms.com/1k47bfuX2SL2DKZtZoJzArg4vgrs', "_blank")} >{translate("Apply for Fall 2024")}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="order-2 col-lg-6 px-0 ml-lg-2">
|
||||
<div className="row m-0">
|
||||
<div className="col-12 col-lg-6 p-0 pr-lg-4">
|
||||
<div className="px-lg-3 pb-3">
|
||||
<img src={require("../static/img/ambassadors/01.svg")} className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3">
|
||||
<h6 className="mb-3">{translate("Apply")}</h6>
|
||||
<p>{translate("Submit an application to be considered for the Campus Ambassador program.")}</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* Hide on large */}
|
||||
<div className="px-lg-3 pb-3 d-lg-none ">
|
||||
<img src={require("../static/img/ambassadors/02.svg")} className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3">
|
||||
<h6 className="mb-3">{translate("Interview")}</h6>
|
||||
<p>{translate("Tell the XRPL community-led panel more about yourself and your interest in the program during an interview.")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-lg-3 pb-3">
|
||||
<img src={require("../static/img/ambassadors/03.svg")} className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3">
|
||||
<h6 className="mb-3">{translate("Join")}</h6>
|
||||
<p>{translate("Congrats on your new role! Join the global cohort of Ambassadors and meet with community participants during onboarding.")}</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* Hide on large */}
|
||||
<div className="p-lg-3 pb-3 d-lg-none">
|
||||
<img src={require("../static/img/ambassadors/04.svg")} className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3">
|
||||
<h6 className="mb-3">{translate("Learn")}</h6>
|
||||
<p> {translate("Participate in personalized learning and training sessions for Ambassadors on the XRPL and blockchain technology.")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* end col 1 */}
|
||||
{/* Show on large */}
|
||||
<div className="col-12 col-lg-6 p-0 pl-lg-4 d-none d-lg-block mt-5">
|
||||
<div className="px-lg-3 pb-3 mt-5">
|
||||
<img src={require("../static/img/ambassadors/02.svg")} className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3">
|
||||
<h6 className="mb-3">{translate("Interview")}</h6>
|
||||
<p>{translate("Tell the XRPL community-led panel more about yourself and your interest in the program during an interview.")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-lg-3 pb-3 ">
|
||||
<img src={require("../static/img/ambassadors/04.svg")} className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3 pb-lg-0">
|
||||
<h6 className="mb-3">{translate("Learn")}</h6>
|
||||
<p className="pb-lg-0">{translate("Participate in personalized learning and training sessions for Ambassadors on the XRPL and blockchain technology.")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* end col 2 */}
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-lg-none order-3 mt-4 pt-3">
|
||||
<button className="btn btn-primary btn-arrow-out" onClick={() => window.open('https://share.hsforms.com/1k47bfuX2SL2DKZtZoJzArg4vgrs', "_blank")} >{translate("Apply for Fall 2024")}</button>
|
||||
</div>
|
||||
{/* flex. Col for mobile. Row for large. on large align content to the center */}
|
||||
<div className="d-flex flex-column flex-lg-row align-items-lg-center">
|
||||
<div className="order-1 mr-lg-4 mb-4 pb-3 mb-lg-0 pb-lg-0 col-lg-6 px-0">
|
||||
<div className="d-flex flex-column-reverse p-lg-3">
|
||||
<h3 className="h4 h2-sm">{translate("Process to become a Campus Ambassador")}</h3>
|
||||
<h6 className="eyebrow mb-3">{translate("How it Works")}</h6>
|
||||
</div>
|
||||
<p className="p-lg-3 mb-2 longform">{translate("Apply now to become an XRPL Campus Ambassador.")}</p>
|
||||
<div className="d-none d-lg-block p-lg-3">
|
||||
<button className="btn btn-primary btn-arrow-out" onClick={() => window.open('https://share.hsforms.com/1k47bfuX2SL2DKZtZoJzArg4vgrs', "_blank")} >{translate("Apply for Fall 2024")}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="order-2 col-lg-6 px-0 ml-lg-2">
|
||||
<div className="row m-0">
|
||||
<div className="col-12 col-lg-6 p-0 pr-lg-4">
|
||||
<div className="px-lg-3 pb-3">
|
||||
<img src={require("../static/img/ambassadors/01.svg")} className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3">
|
||||
<h6 className="mb-3">{translate("Apply")}</h6>
|
||||
<p>{translate("Submit an application to be considered for the Campus Ambassador program.")}</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* Hide on large */}
|
||||
<div className="px-lg-3 pb-3 d-lg-none ">
|
||||
<img src={require("../static/img/ambassadors/02.svg")} className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3">
|
||||
<h6 className="mb-3">{translate("Interview")}</h6>
|
||||
<p>{translate("Tell the XRPL community-led panel more about yourself and your interest in the program during an interview.")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-lg-3 pb-3">
|
||||
<img src={require("../static/img/ambassadors/03.svg")} className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3">
|
||||
<h6 className="mb-3">{translate("Join")}</h6>
|
||||
<p>{translate("Congrats on your new role! Join the global cohort of Ambassadors and meet with community participants during onboarding.")}</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* Hide on large */}
|
||||
<div className="p-lg-3 pb-3 d-lg-none">
|
||||
<img src={require("../static/img/ambassadors/04.svg")} className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3">
|
||||
<h6 className="mb-3">{translate("Learn")}</h6>
|
||||
<p> {translate("Participate in personalized learning and training sessions for Ambassadors on the XRPL and blockchain technology.")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* end col 1 */}
|
||||
{/* Show on large */}
|
||||
<div className="col-12 col-lg-6 p-0 pl-lg-4 d-none d-lg-block mt-5">
|
||||
<div className="px-lg-3 pb-3 mt-5">
|
||||
<img src={require("../static/img/ambassadors/02.svg")} className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3">
|
||||
<h6 className="mb-3">{translate("Interview")}</h6>
|
||||
<p>{translate("Tell the XRPL community-led panel more about yourself and your interest in the program during an interview.")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-lg-3 pb-3 ">
|
||||
<img src={require("../static/img/ambassadors/04.svg")} className="pl-lg-3" />
|
||||
<div className="p-lg-3 pt-3 pb-lg-0">
|
||||
<h6 className="mb-3">{translate("Learn")}</h6>
|
||||
<p className="pb-lg-0">{translate("Participate in personalized learning and training sessions for Ambassadors on the XRPL and blockchain technology.")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* end col 2 */}
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-lg-none order-3 mt-4 pt-3">
|
||||
<button className="btn btn-primary btn-arrow-out" onClick={() => window.open('https://share.hsforms.com/1k47bfuX2SL2DKZtZoJzArg4vgrs', "_blank")} >{translate("Apply for Fall 2024")}</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{/* Image Block */}
|
||||
<div>
|
||||
<img alt="Ripple Conferences and two people Sitting" src={require("../static/img/ambassadors/students-large.png")} className="w-100" />
|
||||
<img alt="Ripple Conferences and two people Sitting" src={require("../static/img/ambassadors/students-large.png")} className="w-100" />
|
||||
</div>
|
||||
{/* Global Community Carousel */}
|
||||
<section className="container-new pt-26">
|
||||
<div className="p-0 col-lg-5">
|
||||
<div className="d-flex flex-column-reverse p-lg-3">
|
||||
<h3 className="h4 h2-sm">{translate("Join a global cohort of Student Ambassadors")}</h3>
|
||||
<h6 className="eyebrow mb-3">{translate("Global Community")}</h6>
|
||||
</div>
|
||||
<div className="p-0 col-lg-5">
|
||||
<div className="d-flex flex-column-reverse p-lg-3">
|
||||
<h3 className="h4 h2-sm">{translate("Join a global cohort of Student Ambassadors")}</h3>
|
||||
<h6 className="eyebrow mb-3">{translate("Global Community")}</h6>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div id="container-scroll">
|
||||
<div className="photobanner">
|
||||
<img src={require("../static/img/ambassadors/locations-row-1.png")} alt="Ambassador locations" height="48px" className="px-5" />
|
||||
<img src={require("../static/img/ambassadors/locations-row-1.png")} alt="Ambassador locations" height="48px" className="px-5" />
|
||||
<img src={require("../static/img/ambassadors/locations-row-1.png")} alt="Ambassador locations" height="48px" className="px-5" />
|
||||
</div>
|
||||
<div className="photobanner photobanner-bottom">
|
||||
<img src={require("../static/img/ambassadors/locations-row-2.png")} alt="Ambassador locations" height="48px" className="px-5" />
|
||||
<img src={require("../static/img/ambassadors/locations-row-2.png")} alt="Ambassador locations" height="48px" className="px-5" />
|
||||
<img src={require("../static/img/ambassadors/locations-row-2.png")} alt="Ambassador locations" height="48px" className="px-5" />
|
||||
</div>
|
||||
<div className="photobanner">
|
||||
<img src={require("../static/img/ambassadors/locations-row-1.png")} alt="Ambassador locations" height="48px" className="px-5" />
|
||||
<img src={require("../static/img/ambassadors/locations-row-1.png")} alt="Ambassador locations" height="48px" className="px-5" />
|
||||
<img src={require("../static/img/ambassadors/locations-row-1.png")} alt="Ambassador locations" height="48px" className="px-5" />
|
||||
</div>
|
||||
<div className="photobanner photobanner-bottom">
|
||||
<img src={require("../static/img/ambassadors/locations-row-2.png")} alt="Ambassador locations" height="48px" className="px-5" />
|
||||
<img src={require("../static/img/ambassadors/locations-row-2.png")} alt="Ambassador locations" height="48px" className="px-5" />
|
||||
<img src={require("../static/img/ambassadors/locations-row-2.png")} alt="Ambassador locations" height="48px" className="px-5" />
|
||||
</div>
|
||||
</div>
|
||||
{/* Connect */}
|
||||
<section className="container-new py-26">
|
||||
{/* flex. Col for mobile. Row for large. on large align content to the center */}
|
||||
<div className="d-flex flex-column flex-lg-row align-items-lg-center">
|
||||
<div className="order-1 mb-4 pb-3 mb-lg-0 pb-lg-0 col-lg-6 px-0">
|
||||
<div className="d-flex flex-column-reverse p-lg-3">
|
||||
<h3 className="h4 h2-sm">{translate("Stay connected to the XRPL Community")}</h3>
|
||||
<h6 className="eyebrow mb-3">{translate("Connect")}</h6>
|
||||
</div>
|
||||
<p className="p-lg-3 mb-2 longform">{translate("To stay up-to-date on the latest activity, meetups, and events of the XRPL Community be sure to follow these channels:")}</p>
|
||||
<div className="d-none d-lg-block p-lg-3">
|
||||
<button className="btn btn-primary btn-arrow-out" onClick={() => window.open('https://share.hsforms.com/1k47bfuX2SL2DKZtZoJzArg4vgrs', "_blank")} >{translate("Apply for Fall 2024")}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="order-2 col-lg-6 px-0 ml-lg-5">
|
||||
<div className="row align-items-center m-0">
|
||||
<div className="col-12 col-lg-6 p-0 pr-lg-4">
|
||||
<div className="p-lg-3 mb-3 pb-3">
|
||||
<img alt="meetup" src={require("../static/img/ambassadors/icon_meetup.svg")} className="mb-3" />
|
||||
<div>
|
||||
<h6 className="mb-3"><a className="btn-arrow" href="https://www.meetup.com/pro/xrpl-community/">{translate("MeetUp")}</a></h6>
|
||||
<p>{translate("Attend an XRPL Meetup in your local area")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-lg-3 mb-3 pb-3">
|
||||
<img alt="devto" src={require("../static/img/ambassadors/icon_devto.svg")} className="mb-3" />
|
||||
<div>
|
||||
<h6 className="mb-3"><a className="btn-arrow" href="https://dev.to/t/xrpl">{translate("Dev.to Blog")}</a></h6>
|
||||
<p>{translate("Read more about the activity of the XRPL Ambassadors")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-lg-6 p-0 pl-lg-4">
|
||||
<div className="p-lg-3 mb-3 pb-3 ">
|
||||
<img alt="discord" src={require("../static/img/ambassadors/icon_discord.svg")} className="mb-3" />
|
||||
<div>
|
||||
<h6 className="mb-3"><a className="btn-arrow" href="https://xrpldevs.org">{translate("Discord")}</a></h6>
|
||||
<p>{translate("Join the conversation on the XRPL Developer Discord")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-lg-none order-3 mt-4 pt-3">
|
||||
<button className="btn btn-primary btn-arrow-out" onClick={() => window.open('https://share.hsforms.com/1k47bfuX2SL2DKZtZoJzArg4vgrs', "_blank")} >{translate("Apply for Fall 2024")}</button>
|
||||
</div>
|
||||
{/* flex. Col for mobile. Row for large. on large align content to the center */}
|
||||
<div className="d-flex flex-column flex-lg-row align-items-lg-center">
|
||||
<div className="order-1 mb-4 pb-3 mb-lg-0 pb-lg-0 col-lg-6 px-0">
|
||||
<div className="d-flex flex-column-reverse p-lg-3">
|
||||
<h3 className="h4 h2-sm">{translate("Stay connected to the XRPL Community")}</h3>
|
||||
<h6 className="eyebrow mb-3">{translate("Connect")}</h6>
|
||||
</div>
|
||||
<p className="p-lg-3 mb-2 longform">{translate("To stay up-to-date on the latest activity, meetups, and events of the XRPL Community be sure to follow these channels:")}</p>
|
||||
<div className="d-none d-lg-block p-lg-3">
|
||||
<button className="btn btn-primary btn-arrow-out" onClick={() => window.open('https://share.hsforms.com/1k47bfuX2SL2DKZtZoJzArg4vgrs', "_blank")} >{translate("Apply for Fall 2024")}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="order-2 col-lg-6 px-0 ml-lg-5">
|
||||
<div className="row align-items-center m-0">
|
||||
<div className="col-12 col-lg-6 p-0 pr-lg-4">
|
||||
<div className="p-lg-3 mb-3 pb-3">
|
||||
<img alt="meetup" src={require("../static/img/ambassadors/icon_meetup.svg")} className="mb-3" />
|
||||
<div>
|
||||
<h6 className="mb-3"><a className="btn-arrow" href="https://www.meetup.com/pro/xrpl-community/">{translate("MeetUp")}</a></h6>
|
||||
<p>{translate("Attend an XRPL Meetup in your local area")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-lg-3 mb-3 pb-3">
|
||||
<img alt="devto" src={require("../static/img/ambassadors/icon_devto.svg")} className="mb-3" />
|
||||
<div>
|
||||
<h6 className="mb-3"><a className="btn-arrow" href="https://dev.to/t/xrpl">{translate("Dev.to Blog")}</a></h6>
|
||||
<p>{translate("Read more about the activity of the XRPL Ambassadors")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-lg-6 p-0 pl-lg-4">
|
||||
<div className="p-lg-3 mb-3 pb-3 ">
|
||||
<img alt="discord" src={require("../static/img/ambassadors/icon_discord.svg")} className="mb-3" />
|
||||
<div>
|
||||
<h6 className="mb-3"><a className="btn-arrow" href="https://xrpldevs.org">{translate("Discord")}</a></h6>
|
||||
<p>{translate("Join the conversation on the XRPL Developer Discord")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-lg-none order-3 mt-4 pt-3">
|
||||
<button className="btn btn-primary btn-arrow-out" onClick={() => window.open('https://share.hsforms.com/1k47bfuX2SL2DKZtZoJzArg4vgrs', "_blank")} >{translate("Apply for Fall 2024")}</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,431 +1,271 @@
|
||||
import * as React from "react";
|
||||
import { useThemeHooks } from '@redocly/theme/core/hooks';
|
||||
import { Link } from '@redocly/theme/components/Link/Link';
|
||||
import { useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
import { HeaderHeroSplitMedia } from "shared/sections/HeaderHeroSplitMedia/HeaderHeroSplitMedia";
|
||||
import {
|
||||
CarouselFeatured,
|
||||
type CarouselSlide,
|
||||
} from "shared/patterns/CarouselFeatured/CarouselFeatured";
|
||||
import { LogoSquareGrid } from "shared/sections/LogoSquareGrid";
|
||||
import FeaturedVideoHero from "shared/sections/FeaturedVideoHero/FeaturedVideoHero";
|
||||
import { LinkTextDirectory } from "shared/sections/LinkTextDirectory/LinkTextDirectory";
|
||||
|
||||
export const frontmatter = {
|
||||
seo: {
|
||||
title: 'Developer Funding',
|
||||
description: "If you’re a software developer or team looking to build your next project or venture on the XRP Ledger (XRPL), there are a number of opportunities to fund your next innovation.",
|
||||
}
|
||||
title: "Developer Funding",
|
||||
description:
|
||||
"Access grants, programs, and tailored support to build the next wave of blockchain innovation on the XRP Ledger.",
|
||||
},
|
||||
};
|
||||
|
||||
false;
|
||||
|
||||
const target = { prefix: "" }; // TODO: fixme
|
||||
|
||||
export default function Funding() {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
return (
|
||||
<div className="landing page-funding">
|
||||
<div>
|
||||
<div className="position-relative d-none-sm">
|
||||
<img
|
||||
alt="purple waves"
|
||||
src={require("../static/img/backgrounds/funding-purple.svg")}
|
||||
className="position-absolute"
|
||||
style={{ top: 0, right: 0 }}
|
||||
/>
|
||||
</div>
|
||||
<section className="container-new py-26 text-lg-center">
|
||||
<div className="p-0 col-lg-6 mx-lg-auto">
|
||||
<div className="d-flex flex-column-reverse">
|
||||
<h1 className="mb-0">
|
||||
{translate("XRPL Developer Funding Programs")}
|
||||
</h1>
|
||||
<h6 className="eyebrow mb-3">{translate("Project Resources")}</h6>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section className="container-new py-26">
|
||||
<div className="p-0 col-lg-6 mx-lg-auto" style={{ maxWidth: 520 }}>
|
||||
<div className="d-flex flex-column-reverse">
|
||||
<h1 className="mb-0 h4 h2-sm">
|
||||
{translate(
|
||||
"Explore funding opportunities for developers and teams"
|
||||
)}
|
||||
</h1>
|
||||
<h6 className="eyebrow mb-3">{translate("Funding Overview")}</h6>
|
||||
</div>
|
||||
<p className="mt-3 py-3 p-0 longform">
|
||||
const carouselSlides: CarouselSlide[] = [
|
||||
{
|
||||
id: "xrpl-grants-accelerator",
|
||||
heading: translate("Explore XRPL Funding and Builders Programs"),
|
||||
features: [
|
||||
{
|
||||
title: translate("XRPL Grants & XRPL Accelerator"),
|
||||
description: (
|
||||
<>
|
||||
{translate(
|
||||
"If you’re a software developer or team looking to build your next project or venture on the XRP Ledger (XRPL), there are a number of opportunities to fund your next innovation."
|
||||
"Fueling innovation across DeFi, tokenization, and payments."
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
{/* Hackathons */}
|
||||
<section className="container-new py-26">
|
||||
{/* flex. Col for mobile. Row for large. on large align content to the center */}
|
||||
<div className="d-flex flex-column flex-lg-row align-items-lg-center">
|
||||
<div
|
||||
className="order-1 order-lg-2 mb-4 pb-3 mb-lg-0 pb-lg-0 col-lg-6 px-0"
|
||||
style={{ maxWidth: 520 }}
|
||||
>
|
||||
<div className="d-flex flex-column-reverse p-lg-3">
|
||||
<h3 className="h4 h2-sm">{translate("XRPL Hackathons")}</h3>
|
||||
<h6 className="eyebrow mb-3">{translate("Join an Event")}</h6>
|
||||
</div>
|
||||
<p className="p-lg-3 mb-2 longform">
|
||||
{translate(
|
||||
"Hackathons are open to all developers to explore and invent a project on the XRP Ledger. Visit the events page for updates on upcoming hackathons."
|
||||
)}
|
||||
</p>
|
||||
<div className="d-none d-lg-block p-lg-3">
|
||||
<Link className="btn btn-primary btn-arrow" to="/community/events">
|
||||
{translate("See Upcoming Events")}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="order-2 order-lg-1 col-lg-6 px-0">
|
||||
<div className="row align-items-center m-0 funding-list">
|
||||
{/* funding list */}
|
||||
<div className="col-12 col-lg-6 p-0">
|
||||
<div className="px-lg-3 pb-3">
|
||||
<img alt="user" id="funding-01" />
|
||||
<div className="pt-3">
|
||||
<h6 className="mb-3">{translate("Best for")}</h6>
|
||||
<p>
|
||||
{translate(
|
||||
"Software developers and teams building directly on the XRP Ledger"
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* Hide on large */}
|
||||
<div className="px-lg-3 pb-3 d-lg-none">
|
||||
<img alt="book" id="funding-02" />
|
||||
<div className="pt-3">
|
||||
<h6 className="mb-3">{translate("Required")}</h6>
|
||||
<p>{translate("Some coding experience")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-lg-3 pb-3 pt-lg-5">
|
||||
<img alt="arrow" id="funding-03" />
|
||||
<div className="pt-3">
|
||||
<h6 className="mb-3">{translate("Level")}</h6>
|
||||
<p>{translate("XRPL beginner to advanced developers")}</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* Hide on large */}
|
||||
<div className="px-lg-3 pb-3 d-lg-none">
|
||||
<img alt="dollar sign" id="funding-04" />
|
||||
<div className="pt-3">
|
||||
<h6 className="mb-3">{translate("Funding Levels")}</h6>
|
||||
<p>{translate("Prize money and awards")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* end col 1 */}
|
||||
{/* Show on large */}
|
||||
<div className="col-12 col-lg-6 p-0 d-none d-lg-block">
|
||||
<div className="px-lg-3 pb-3 pt-5 mt-5">
|
||||
<div className="pt-1 mt-3">
|
||||
<img alt="book" id="funding-02" />
|
||||
<div className="pt-3">
|
||||
<h6 className="mb-3">{translate("Required")}</h6>
|
||||
<p>{translate("Some coding experience")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-lg-3 pb-3 pt-5">
|
||||
<img alt="dollar sign" id="funding-04" />
|
||||
<div className="pt-3">
|
||||
<h6 className="mb-3">{translate("Funding Levels")}</h6>
|
||||
<p>{translate("Prize money and awards")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* end col 2 */}
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-lg-none order-3 mt-4 pt-3">
|
||||
<Link className="btn btn-primary btn-arrow" to="/community/events">
|
||||
{translate("See Upcoming Events")}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{/* Eligibility */}
|
||||
<section className="container-new py-26">
|
||||
{/* flex. Col for mobile. Row for large. on large align content to the center */}
|
||||
<div className="d-flex flex-column flex-lg-row align-items-lg-center mr-lg-4">
|
||||
<div className="order-1 mb-4 pb-3 mb-lg-0 pb-lg-0 col-lg-6 px-0 p-lg-3">
|
||||
<div className="d-flex flex-column-reverse py-lg-3">
|
||||
<h3 className="h4 h2-sm">{translate("XRPL Grants")}</h3>
|
||||
<h6 className="eyebrow mb-3">
|
||||
{translate("Fund Your Project")}
|
||||
</h6>
|
||||
</div>
|
||||
<p className="py-lg-3 mb-2 longform" style={{ maxWidth: 520 }}>
|
||||
{translate(
|
||||
"Developer grants for projects that contribute to the growing XRP Ledger community."
|
||||
)}
|
||||
</p>
|
||||
<div className="mt-4 pt-3" style={{ maxWidth: 520 }}>
|
||||
<span className="h6" style={{ fontSize: "1rem" }}>
|
||||
{translate("Past awardees include:")}
|
||||
</span>
|
||||
<div className="mb-4 py-3" id="xrplGrantsDark" />
|
||||
</div>
|
||||
<div className="d-none d-lg-block py-lg-3">
|
||||
<a
|
||||
className="btn btn-primary btn-arrow-out"
|
||||
target="_blank"
|
||||
href="https://xrplgrants.org/"
|
||||
>
|
||||
{translate("Visit XRPL Grants")}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="order-2 col-lg-6 px-0 pl-lg-3">
|
||||
<div className="row align-items-center m-0 funding-list">
|
||||
{/* funding list */}
|
||||
<div className="col-12 col-lg-6 p-0">
|
||||
<div className="px-lg-3 pb-3">
|
||||
<img alt="user" id="funding-01" />
|
||||
<div className="pt-3">
|
||||
<h6 className="mb-3">{translate("Best for")}</h6>
|
||||
<p>
|
||||
{translate(
|
||||
"Software developers, teams, and start-ups building directly on the XRP Ledger"
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* Hide on large */}
|
||||
<div className="px-lg-3 pb-3 d-lg-none">
|
||||
<img alt="book" id="funding-02" />
|
||||
<div className="pt-3">
|
||||
<h6 className="mb-3">{translate("Required")}</h6>
|
||||
<p>
|
||||
<span style={{ color: "#7919FF" }}>•</span>{" "}
|
||||
{translate("Coding experience")}
|
||||
<br />
|
||||
<span style={{ color: "#7919FF" }}>•</span>{" "}
|
||||
{translate("Github repository")}
|
||||
<br />
|
||||
<span style={{ color: "#7919FF" }}>•</span>{" "}
|
||||
{translate("Project narrative/description")}
|
||||
<br />
|
||||
<span style={{ color: "#7919FF" }}>•</span>{" "}
|
||||
{translate("At least one developer on the core team")}
|
||||
<br />
|
||||
<span style={{ color: "#7919FF" }}>•</span>{" "}
|
||||
{translate("Budget and milestones")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-lg-3 pb-3 pt-lg-5">
|
||||
<img alt="arrow" id="funding-03" />
|
||||
<div className="pt-3">
|
||||
<h6 className="mb-3">{translate("Level")}</h6>
|
||||
<p>
|
||||
{translate("XRPL intermediate to advanced developers")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* Hide on large */}
|
||||
<div className="px-lg-3 pb-3 d-lg-none">
|
||||
<img alt="dollar sign" id="funding-04" />
|
||||
<div className="pt-3">
|
||||
<h6 className="mb-3">{translate("Funding Levels")}</h6>
|
||||
<p>{translate("$10,000 - $200,000")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* end col 1 */}
|
||||
{/* Show on large */}
|
||||
<div className="col-12 col-lg-6 p-0 d-none d-lg-block">
|
||||
<div className="px-lg-3 pb-3 pt-5 mt-5">
|
||||
<div className="pt-1 mt-3">
|
||||
<img alt="book" id="funding-02" />
|
||||
<div className="pt-3">
|
||||
<h6 className="mb-3">{translate("Required")}</h6>
|
||||
<p>
|
||||
<span style={{ color: "#7919FF" }}>•</span>{" "}
|
||||
{translate("Coding experience")}
|
||||
<br />
|
||||
<span style={{ color: "#7919FF" }}>•</span>{" "}
|
||||
{translate("Github repository")}
|
||||
<br />
|
||||
<span style={{ color: "#7919FF" }}>•</span>{" "}
|
||||
{translate("Project narrative/description")}
|
||||
<br />
|
||||
<span style={{ color: "#7919FF" }}>•</span>{" "}
|
||||
{translate("At least one developer on the core team")}
|
||||
<br />
|
||||
<span style={{ color: "#7919FF" }}>•</span>{" "}
|
||||
{translate("Budget and milestones")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-lg-3 pb-3 pt-5">
|
||||
<img alt="dollar sign" id="funding-04" />
|
||||
<div className="pt-3">
|
||||
<h6 className="mb-3">{translate("Funding Levels")}</h6>
|
||||
<p>{translate("$10,000 - $200,000")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* end col 2 */}
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-lg-none order-3 mt-4 pt-3">
|
||||
<a
|
||||
className="btn btn-primary btn-arrow-out"
|
||||
target="_blank"
|
||||
href="https://xrplgrants.org/"
|
||||
>
|
||||
{translate("Visit XRPL Grants")}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{/* Accelerator */}
|
||||
<section className="container-new py-26">
|
||||
{/* flex. Col for mobile. Row for large. on large align content to the center */}
|
||||
<div className="d-flex flex-column flex-lg-row align-items-lg-center">
|
||||
<div
|
||||
className="order-1 order-lg-2 mb-4 pb-3 mb-lg-0 pb-lg-0 col-lg-6 px-0"
|
||||
style={{ maxWidth: 520 }}
|
||||
>
|
||||
<div className="d-flex flex-column-reverse p-lg-3">
|
||||
<h3 className="h4 h2-sm">{translate("XRPL Accelerator")}</h3>
|
||||
<h6 className="eyebrow mb-3">
|
||||
{translate("Advance your project")}
|
||||
</h6>
|
||||
</div>
|
||||
<p className="p-lg-3 mb-2 longform">
|
||||
{translate(
|
||||
"12-week program for entrepreneurs building on the XRP Ledger to scale their projects into thriving businesses."
|
||||
)}
|
||||
</p>
|
||||
<div className="d-none d-lg-block p-lg-3">
|
||||
<a
|
||||
className="btn btn-primary btn-arrow"
|
||||
href="https://xrplaccelerator.org/"
|
||||
>
|
||||
{translate("View XRPL Accelerator")}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="order-2 order-lg-1 col-lg-6 px-0">
|
||||
<div className="row align-items-center m-0 funding-list">
|
||||
{/* funding list */}
|
||||
<div className="col-12 col-lg-6 p-0">
|
||||
<div className="px-lg-3 pb-3">
|
||||
<img alt="user" id="funding-01" />
|
||||
<div className="pt-3">
|
||||
<h6 className="mb-3">{translate("Best for")}</h6>
|
||||
<p>
|
||||
{translate(
|
||||
"Start-ups building scalable products on XRPL that can capture a large market opportunity"
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* Hide on large */}
|
||||
<div className="px-lg-3 pb-3 d-lg-none">
|
||||
<img alt="book" id="funding-02" />
|
||||
<div className="pt-3">
|
||||
<h6 className="mb-3">{translate("Required")}</h6>
|
||||
<p>
|
||||
<span style={{ color: "#7919FF" }}>•</span>{" "}
|
||||
{translate("Strong founding team")}
|
||||
<br />
|
||||
<span style={{ color: "#7919FF" }}>•</span>{" "}
|
||||
{translate("Bold, ambitious vision")}
|
||||
<br />
|
||||
<span style={{ color: "#7919FF" }}>•</span>{" "}
|
||||
{translate("Ideally an MVP and monetization strategy")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-lg-3 pb-3 pt-lg-5">
|
||||
<img alt="arrow" id="funding-03" />
|
||||
<div className="pt-3">
|
||||
<h6 className="mb-3">{translate("Level")}</h6>
|
||||
<p>
|
||||
<span style={{ color: "#7919FF" }}>•</span>{" "}
|
||||
{translate("XRPL advanced developers")}
|
||||
<br />
|
||||
<span style={{ color: "#7919FF" }}>•</span>{" "}
|
||||
{translate("Business acumen")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* Hide on large */}
|
||||
<div className="px-lg-3 pb-3 d-lg-none">
|
||||
<img alt="dollar sign" id="funding-04" />
|
||||
<div className="pt-3">
|
||||
<h6 className="mb-3">{translate("Funding Levels")}</h6>
|
||||
<p>
|
||||
{translate(
|
||||
"$50,000 (grant) + pitch for venture funding"
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* end col 1 */}
|
||||
{/* Show on large */}
|
||||
<div className="col-12 col-lg-6 p-0 d-none d-lg-block">
|
||||
<div className="px-lg-3 pb-3 pt-5 mt-5">
|
||||
<div className="pt-1 mt-3">
|
||||
<img alt="book" id="funding-02" />
|
||||
<div className="pt-3">
|
||||
<h6 className="mb-3">{translate("Required")}</h6>
|
||||
<p>
|
||||
<span style={{ color: "#7919FF" }}>•</span>{" "}
|
||||
{translate("Strong founding team")}
|
||||
<br />
|
||||
<span style={{ color: "#7919FF" }}>•</span>{" "}
|
||||
{translate("Bold, ambitious vision")}
|
||||
<br />
|
||||
<span style={{ color: "#7919FF" }}>•</span>{" "}
|
||||
{translate(
|
||||
"Ideally an MVP and monetization strategy"
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-lg-3 pb-3 pt-5">
|
||||
<img alt="dollar sign" id="funding-04" />
|
||||
<div className="pt-3">
|
||||
<h6 className="mb-3">{translate("Funding Levels")}</h6>
|
||||
<p>
|
||||
{translate(
|
||||
"$50,000 (grant) + pitch for venture funding"
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* end col 2 */}
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-lg-none order-3 mt-4 pt-3">
|
||||
<a
|
||||
className="btn btn-primary btn-arrow"
|
||||
href="https://xrplaccelerator.org/"
|
||||
>
|
||||
{translate("View XRPL Accelerator")}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div className="position-relative d-none-sm">
|
||||
<img
|
||||
alt="orange waves"
|
||||
src={require("../static/img/backgrounds/funding-orange.svg")}
|
||||
id="funding-orange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
{translate("→ Up to $200K in milestone-based funding")}
|
||||
<br />
|
||||
{translate("→ Open to global teams building with XRPL")}
|
||||
<br />
|
||||
{translate("→ Focused on DeFi, RWA, payments, trade finance")}
|
||||
<br />
|
||||
{translate("→ Grants available for MVPs")}
|
||||
<br />
|
||||
{translate("→ Unified application for all XRPL programs")}
|
||||
<br />
|
||||
{translate(
|
||||
"→ A 12-week, hybrid accelerator program supporting startups and institutional builders."
|
||||
)}
|
||||
</>
|
||||
),
|
||||
},
|
||||
],
|
||||
buttons: [
|
||||
{ label: translate("Access Now"), href: "https://xrplgrants.org/" },
|
||||
],
|
||||
imageSrc: require("../static/img/bds-2026/community-developer-funding-carousel-1.jpg"),
|
||||
imageAlt: translate("XRPL Grants and Accelerator"),
|
||||
},
|
||||
{
|
||||
id: "xrpl-student-builder-residency",
|
||||
heading: translate("Explore XRPL Funding and Builders Programs"),
|
||||
features: [
|
||||
{
|
||||
title: translate("XRPL Student Builder Residency"),
|
||||
description: (
|
||||
<>
|
||||
{translate(
|
||||
"Inviting highly motivated University Builders from technical disciplines to build on the XRPL in a 4-week accelerated learning program."
|
||||
)}
|
||||
<br />
|
||||
<br />
|
||||
{translate("→ Deploy and scale high-impact dApps on the XRPL")}
|
||||
<br />
|
||||
{translate(
|
||||
"→ Engage in one-on-one mentorship with expert blockchain developers"
|
||||
)}
|
||||
<br />
|
||||
{translate(
|
||||
"→ Expand your network within the XRPL community and beyond"
|
||||
)}
|
||||
</>
|
||||
),
|
||||
},
|
||||
],
|
||||
buttons: [
|
||||
{
|
||||
label: translate("Access Now"),
|
||||
href: "https://xrpl.org/community/ambassadors",
|
||||
},
|
||||
],
|
||||
imageSrc: require("../static/img/bds-2026/community-developer-funding-carousel-2.jpg"),
|
||||
imageAlt: translate("XRPL Student Builder Residency"),
|
||||
},
|
||||
{
|
||||
id: "xrpl-commons",
|
||||
heading: translate("Explore XRPL Funding and Builders Programs"),
|
||||
features: [
|
||||
{
|
||||
title: translate("XRPL Commons"),
|
||||
description: (
|
||||
<>
|
||||
{translate(
|
||||
"Supporting the global XRPL community through education, innovation, and social impact."
|
||||
)}
|
||||
<br />
|
||||
<br />
|
||||
{translate("→ In-person builder support at XRPL Commons hub")}
|
||||
<br />
|
||||
{translate(
|
||||
"→ Programming for startups, researchers, NGOs, and investors"
|
||||
)}
|
||||
<br />
|
||||
{translate(
|
||||
"→ Partnerships with universities and corporations underway"
|
||||
)}
|
||||
<br />
|
||||
→{" "}
|
||||
<a href="https://www.xrpl-commons.org/residency">
|
||||
{translate("The Aquarium Residency")}
|
||||
</a>{" "}
|
||||
{translate("- 12-week onsite entrepreneurial program in Paris")}
|
||||
<br />
|
||||
→{" "}
|
||||
<a href="https://glow.xrpl-commons.org/">{translate("GLOW")}</a>{" "}
|
||||
{translate(
|
||||
"– rewards program for recognizing developer contributions to the XRPL"
|
||||
)}
|
||||
</>
|
||||
),
|
||||
},
|
||||
],
|
||||
buttons: [
|
||||
{
|
||||
label: translate("Access Now"),
|
||||
href: "https://www.xrpl-commons.org/",
|
||||
},
|
||||
],
|
||||
imageSrc: require("../static/img/bds-2026/community-developer-funding-carousel-3.jpg"),
|
||||
imageAlt: translate("XRPL Commons"),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="landing">
|
||||
<HeaderHeroSplitMedia
|
||||
title={translate("Bring Your Vision to Life")}
|
||||
subtitle={translate(
|
||||
"Access grants, programs, and tailored support to build the next wave of blockchain innovation on the XRP Ledger."
|
||||
)}
|
||||
media={{
|
||||
src: require("../static/img/bds-2026/community-developer-funding-hero-media.jpg"),
|
||||
alt: translate("Bring Your Vision to Life"),
|
||||
}}
|
||||
/>
|
||||
|
||||
<CarouselFeatured slides={carouselSlides} background="yellow" />
|
||||
|
||||
<LogoSquareGrid
|
||||
variant="gray"
|
||||
heading={translate("Shaping the Future Together")}
|
||||
description={translate(
|
||||
"Explore some of the innovative teams and how they are making a real-world difference in the financial landscape today."
|
||||
)}
|
||||
buttons={[
|
||||
{
|
||||
label: translate("Explore Awardees"),
|
||||
href: "https://xrplgrants.org/awardees",
|
||||
},
|
||||
]}
|
||||
logos={[
|
||||
{
|
||||
logo: require("../static/img/logos/black/link.png"),
|
||||
alt: translate("LINK"),
|
||||
href: "https://www.linkio.world/",
|
||||
},
|
||||
{
|
||||
logo: require("../static/img/logos/black/propto.png"),
|
||||
alt: translate("Propto"),
|
||||
href: "https://www.propto.com/",
|
||||
},
|
||||
{
|
||||
logo: require("../static/img/logos/black/t54.png"),
|
||||
alt: translate("t54"),
|
||||
href: "https://www.t54.ai/",
|
||||
},
|
||||
{
|
||||
logo: require("../static/img/logos/black/anodos.png"),
|
||||
alt: translate("Anodos"),
|
||||
href: "https://anodos.finance/",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<FeaturedVideoHero
|
||||
headline={translate(
|
||||
"Unveil Your Project at an XRPL Accelerator Demo Day"
|
||||
)}
|
||||
video={{
|
||||
source: {
|
||||
type: "embed",
|
||||
embedUrl:
|
||||
"https://www.youtube.com/embed/videoseries?list=PLJQ55Tj1hIVZtJ_JdTvSum2qMTsedWkNi",
|
||||
},
|
||||
coverImage: {
|
||||
src: require("../static/img/bds-2026/community-developer-funding-video-poster.jpg"),
|
||||
alt: translate("XRPL Accelerator Demo Day"),
|
||||
},
|
||||
}}
|
||||
links={[
|
||||
{
|
||||
label: translate("Read More"),
|
||||
href: "https://dev.to/ripplexdev/inside-xrpl-accelerators-demo-day-at-the-dubai-fintech-summit-1be9",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<LinkTextDirectory
|
||||
heading={translate(
|
||||
"Explore Opportunities to Build and Get Started Today"
|
||||
)}
|
||||
description={translate(
|
||||
"XRP Ledger is a compliance-focused blockchain where financial applications come to life."
|
||||
)}
|
||||
cards={[
|
||||
{
|
||||
heading: translate(
|
||||
"Get XRPL Grants Updates Delivered to Your Inbox"
|
||||
),
|
||||
description: translate(
|
||||
"Sign up to ensure you get the very latest Grants updates."
|
||||
),
|
||||
buttons: [
|
||||
{
|
||||
label: translate("Sign Up"),
|
||||
href: "https://xrpl.org/docs/tutorials/how-tos/use-tokens/trade-in-the-decentralized-exchange",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: translate(
|
||||
"Get Your Questions Answered by XRPL Commons"
|
||||
),
|
||||
description: translate(
|
||||
"Do you have burning questions related to XRPL Commons?"
|
||||
),
|
||||
buttons: [
|
||||
{
|
||||
label: translate("Contact"),
|
||||
href: "https://discord.gg/sfX3ERAMjH",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: translate("Explore Previous Awardees"),
|
||||
description: translate(
|
||||
"Learn about what it takes to get comprehensive support!"
|
||||
),
|
||||
buttons: [
|
||||
{
|
||||
label: translate("Explore"),
|
||||
href: "https://xrpl.org/about/uses",
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1430,311 +1430,342 @@ export default function Events() {
|
||||
|
||||
return (
|
||||
<div className="landing page-events">
|
||||
<div>
|
||||
<div className="position-relative d-none-sm">
|
||||
<img
|
||||
alt="orange waves"
|
||||
src={require("../static/img/backgrounds/events-orange.svg")}
|
||||
id="events-orange"
|
||||
/>
|
||||
<section className="text-center py-26">
|
||||
<div className="mx-auto text-center col-lg-5">
|
||||
<div className="d-flex flex-column-reverse">
|
||||
<h1 className="mb-0">
|
||||
{translate("Find the XRPL Community Around the World")}
|
||||
</h1>
|
||||
<h6 className="mb-3 eyebrow">{translate("Events")}</h6>
|
||||
</div>
|
||||
</div>
|
||||
<section className="text-center py-26">
|
||||
<div className="mx-auto text-center col-lg-5">
|
||||
</section>
|
||||
<section className="container-new py-26">
|
||||
<div className="event-hero card-grid card-grid-2xN">
|
||||
<div className="pe-2 col">
|
||||
<img
|
||||
alt="xrp ledger events hero"
|
||||
src={require("../static/img/events/xrp-community-night.png")}
|
||||
className="w-100"
|
||||
/>
|
||||
</div>
|
||||
<div className="pt-5 pe-2 col">
|
||||
<div className="d-flex flex-column-reverse">
|
||||
<h1 className="mb-0">
|
||||
{translate("Find the XRPL Community Around the World")}
|
||||
</h1>
|
||||
<h6 className="mb-3 eyebrow">{translate("Events")}</h6>
|
||||
<h2 className="mb-8 h4 h2-sm">
|
||||
{translate("XRP Community Night NYC")}
|
||||
</h2>
|
||||
<h6 className="mb-3 eyebrow">{translate("Save the Date")}</h6>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section className="container-new py-26">
|
||||
<div className="event-hero card-grid card-grid-2xN">
|
||||
<div className="pr-2 col">
|
||||
<img
|
||||
alt="xrp ledger events hero"
|
||||
src={require("../static/img/events/xrpl-hero.png")}
|
||||
className="w-100"
|
||||
/>
|
||||
</div>
|
||||
<div className="pt-5 pr-2 col">
|
||||
<div className="d-flex flex-column-reverse">
|
||||
<h2 className="mb-8 h4 h2-sm">
|
||||
{translate("XRP Community Night Denver")}
|
||||
</h2>
|
||||
<h6 className="mb-3 eyebrow">{translate("Save the Date")}</h6>
|
||||
</div>
|
||||
<p className="mb-4">
|
||||
{translate(
|
||||
"Attending ETHDenver? Join us for an evening with the XRP community in Denver. Connect with the users, builders and projects innovating with and utilizing XRP."
|
||||
)}
|
||||
</p>
|
||||
<div className=" my-3 event-small-gray">
|
||||
{translate("Location: Denver, CO")}
|
||||
</div>
|
||||
<div className="py-2 my-3 event-small-gray">
|
||||
{translate("February 18, 2026")}
|
||||
</div>
|
||||
<div className="d-lg-block">
|
||||
<a
|
||||
className="btn btn-primary btn-arrow-out"
|
||||
target="_blank"
|
||||
href="https://luma.com/chz145tf?utm_source=xprlorg"
|
||||
>
|
||||
{translate("Register Now")}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{/* Upcoming Events */}
|
||||
<section className="container-new py-26" id="upcoming-events">
|
||||
<div className="p-0 pb-2 mb-4 d-flex flex-column-reverse col-lg-6 pr-lg-5">
|
||||
<h3 className="h4 h2-sm">
|
||||
<p className="mb-4">
|
||||
{translate(
|
||||
"Check out meetups, hackathons, and other events hosted by the XRPL Community"
|
||||
"Join the XRP community in NYC—meet builders, users, and projects innovating on the XRP Ledger."
|
||||
)}
|
||||
</h3>
|
||||
<h6 className="mb-3 eyebrow">{translate("Upcoming Events")}</h6>
|
||||
</div>
|
||||
<div className="filter row col-12 mt-lg-5 d-flex flex-column">
|
||||
<h6 className="mb-3">{translate("Filter By:")}</h6>
|
||||
<div>
|
||||
<div className="form-check form-check-inline">
|
||||
<input
|
||||
defaultValue="conference"
|
||||
id="conference-upcoming"
|
||||
name="conference-upcoming"
|
||||
type="checkbox"
|
||||
className="events-filter"
|
||||
checked={upcomingFilters.conference}
|
||||
onChange={handleUpcomingFilterChange}
|
||||
/>
|
||||
<label htmlFor="conference-upcoming">
|
||||
{translate("Conference")}
|
||||
</label>
|
||||
</div>
|
||||
<div className="form-check form-check-inline">
|
||||
<input
|
||||
defaultValue="meetup"
|
||||
id="meetup-upcoming"
|
||||
name="meetup-upcoming"
|
||||
type="checkbox"
|
||||
className="events-filter"
|
||||
checked={upcomingFilters.meetup}
|
||||
onChange={handleUpcomingFilterChange}
|
||||
/>
|
||||
<label htmlFor="meetup-upcoming">{translate("Meetups")}</label>
|
||||
</div>
|
||||
<div className="form-check form-check-inline">
|
||||
<input
|
||||
defaultValue="hackathon"
|
||||
id="hackathon-upcoming"
|
||||
name="hackathon-upcoming"
|
||||
type="checkbox"
|
||||
className="events-filter"
|
||||
checked={upcomingFilters.hackathon}
|
||||
onChange={handleUpcomingFilterChange}
|
||||
/>
|
||||
<label htmlFor="hackathon-upcoming">
|
||||
{translate("Hackathons")}
|
||||
</label>
|
||||
</div>
|
||||
<div className="form-check form-check-inline">
|
||||
<input
|
||||
defaultValue="ama"
|
||||
id="ama-upcoming"
|
||||
name="ama-upcoming"
|
||||
type="checkbox"
|
||||
className="events-filter"
|
||||
checked={upcomingFilters.ama}
|
||||
onChange={handleUpcomingFilterChange}
|
||||
/>
|
||||
<label htmlFor="ama-upcoming">{translate("AMAs")}</label>
|
||||
</div>
|
||||
<div className="form-check form-check-inline">
|
||||
<input
|
||||
defaultValue="cc"
|
||||
id="cc-upcoming"
|
||||
name="cc-upcoming"
|
||||
type="checkbox"
|
||||
className="events-filter"
|
||||
checked={upcomingFilters.cc}
|
||||
onChange={handleUpcomingFilterChange}
|
||||
/>
|
||||
<label htmlFor="cc-upcoming">
|
||||
{translate("Community Calls")}
|
||||
</label>
|
||||
</div>
|
||||
<div className="form-check form-check-inline">
|
||||
<input
|
||||
defaultValue="zone"
|
||||
id="zone-upcoming"
|
||||
name="zone-upcoming"
|
||||
type="checkbox"
|
||||
className="events-filter"
|
||||
checked={upcomingFilters.zone}
|
||||
onChange={handleUpcomingFilterChange}
|
||||
/>
|
||||
<label htmlFor="zone-upcoming">{translate("XRPL Zone")}</label>
|
||||
</div>
|
||||
<div className="form-check form-check-inline">
|
||||
<input
|
||||
defaultValue="info"
|
||||
id="info-upcoming"
|
||||
name="info-upcoming"
|
||||
type="checkbox"
|
||||
className="events-filter"
|
||||
checked={upcomingFilters["info"]}
|
||||
onChange={handleUpcomingFilterChange}
|
||||
/>
|
||||
<label htmlFor="info-upcoming">
|
||||
{translate("Info Session")}
|
||||
</label>
|
||||
</div>
|
||||
</p>
|
||||
<div className=" my-3 event-small-gray">
|
||||
{translate("Location: New York, NY")}
|
||||
</div>
|
||||
<div className="py-2 my-3 event-small-gray">
|
||||
{translate("November 5, 2025")}
|
||||
</div>
|
||||
<div className="d-lg-block">
|
||||
<a
|
||||
className="btn btn-primary btn-arrow-out"
|
||||
target="_blank"
|
||||
href="https://lu.ma/g5uja58m?utm_source=xrpleventspage"
|
||||
>
|
||||
{translate("Register Now")}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{/* # Available Types - conference, hackathon, ama, cc, zone, meetup, info */}
|
||||
<div className="mt-2 row row-cols-1 row-cols-lg-3 card-deck">
|
||||
{filteredUpcoming.map((event, i) => (
|
||||
</div>
|
||||
</section>
|
||||
<section className="container-new py-26">
|
||||
<div className="event-hero card-grid card-grid-2xN">
|
||||
<div className="pr-2 col">
|
||||
<img
|
||||
alt="xrp ledger events hero"
|
||||
src={require("../static/img/events/xrpl-hero.png")}
|
||||
className="w-100"
|
||||
/>
|
||||
</div>
|
||||
<div className="pt-5 pr-2 col">
|
||||
<div className="d-flex flex-column-reverse">
|
||||
<h2 className="mb-8 h4 h2-sm">
|
||||
{translate("XRP Community Night Denver")}
|
||||
</h2>
|
||||
<h6 className="mb-3 eyebrow">{translate("Save the Date")}</h6>
|
||||
</div>
|
||||
<p className="mb-4">
|
||||
{translate(
|
||||
"Attending ETHDenver? Join us for an evening with the XRP community in Denver. Connect with the users, builders and projects innovating with and utilizing XRP."
|
||||
)}
|
||||
</p>
|
||||
<div className=" my-3 event-small-gray">
|
||||
{translate("Location: Denver, CO")}
|
||||
</div>
|
||||
<div className="py-2 my-3 event-small-gray">
|
||||
{translate("February 18, 2026")}
|
||||
</div>
|
||||
<div className="d-lg-block">
|
||||
<a
|
||||
key={event.name + i}
|
||||
className={`event-card ${event.type}`}
|
||||
className="btn btn-primary btn-arrow-out"
|
||||
target="_blank"
|
||||
href="https://luma.com/chz145tf?utm_source=xprlorg"
|
||||
>
|
||||
{translate("Register Now")}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{/* Upcoming Events */}
|
||||
<section className="container-new py-26" id="upcoming-events">
|
||||
<div className="p-0 pb-2 mb-4 d-flex flex-column-reverse col-lg-6 pr-lg-5">
|
||||
<h3 className="h4 h2-sm">
|
||||
{translate(
|
||||
"Check out meetups, hackathons, and other events hosted by the XRPL Community"
|
||||
)}
|
||||
</h3>
|
||||
<h6 className="mb-3 eyebrow">{translate("Upcoming Events")}</h6>
|
||||
</div>
|
||||
<div className="filter row col-12 mt-lg-5 d-flex flex-column">
|
||||
<h6 className="mb-3">{translate("Filter By:")}</h6>
|
||||
<div>
|
||||
<div className="form-check form-check-inline">
|
||||
<input
|
||||
defaultValue="conference"
|
||||
id="conference-upcoming"
|
||||
name="conference-upcoming"
|
||||
type="checkbox"
|
||||
className="events-filter"
|
||||
checked={upcomingFilters.conference}
|
||||
onChange={handleUpcomingFilterChange}
|
||||
/>
|
||||
<label htmlFor="conference-upcoming">
|
||||
{translate("Conference")}
|
||||
</label>
|
||||
</div>
|
||||
<div className="form-check form-check-inline">
|
||||
<input
|
||||
defaultValue="meetup"
|
||||
id="meetup-upcoming"
|
||||
name="meetup-upcoming"
|
||||
type="checkbox"
|
||||
className="events-filter"
|
||||
checked={upcomingFilters.meetup}
|
||||
onChange={handleUpcomingFilterChange}
|
||||
/>
|
||||
<label htmlFor="meetup-upcoming">{translate("Meetups")}</label>
|
||||
</div>
|
||||
<div className="form-check form-check-inline">
|
||||
<input
|
||||
defaultValue="hackathon"
|
||||
id="hackathon-upcoming"
|
||||
name="hackathon-upcoming"
|
||||
type="checkbox"
|
||||
className="events-filter"
|
||||
checked={upcomingFilters.hackathon}
|
||||
onChange={handleUpcomingFilterChange}
|
||||
/>
|
||||
<label htmlFor="hackathon-upcoming">
|
||||
{translate("Hackathons")}
|
||||
</label>
|
||||
</div>
|
||||
<div className="form-check form-check-inline">
|
||||
<input
|
||||
defaultValue="ama"
|
||||
id="ama-upcoming"
|
||||
name="ama-upcoming"
|
||||
type="checkbox"
|
||||
className="events-filter"
|
||||
checked={upcomingFilters.ama}
|
||||
onChange={handleUpcomingFilterChange}
|
||||
/>
|
||||
<label htmlFor="ama-upcoming">{translate("AMAs")}</label>
|
||||
</div>
|
||||
<div className="form-check form-check-inline">
|
||||
<input
|
||||
defaultValue="cc"
|
||||
id="cc-upcoming"
|
||||
name="cc-upcoming"
|
||||
type="checkbox"
|
||||
className="events-filter"
|
||||
checked={upcomingFilters.cc}
|
||||
onChange={handleUpcomingFilterChange}
|
||||
/>
|
||||
<label htmlFor="cc-upcoming">
|
||||
{translate("Community Calls")}
|
||||
</label>
|
||||
</div>
|
||||
<div className="form-check form-check-inline">
|
||||
<input
|
||||
defaultValue="zone"
|
||||
id="zone-upcoming"
|
||||
name="zone-upcoming"
|
||||
type="checkbox"
|
||||
className="events-filter"
|
||||
checked={upcomingFilters.zone}
|
||||
onChange={handleUpcomingFilterChange}
|
||||
/>
|
||||
<label htmlFor="zone-upcoming">{translate("XRPL Zone")}</label>
|
||||
</div>
|
||||
<div className="form-check form-check-inline">
|
||||
<input
|
||||
defaultValue="info"
|
||||
id="info-upcoming"
|
||||
name="info-upcoming"
|
||||
type="checkbox"
|
||||
className="events-filter"
|
||||
checked={upcomingFilters["info"]}
|
||||
onChange={handleUpcomingFilterChange}
|
||||
/>
|
||||
<label htmlFor="info-upcoming">
|
||||
{translate("Info Session")}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* # Available Types - conference, hackathon, ama, cc, zone, meetup, info */}
|
||||
<div className="row row-cols-1 row-cols-lg-3 g-4 mt-2">
|
||||
{filteredUpcoming.map((event, i) => (
|
||||
<div key={event.name + i} className="col">
|
||||
<a
|
||||
className={`event-card ${event.type} h-100`}
|
||||
href={event.link}
|
||||
style={{}}
|
||||
target="_blank"
|
||||
>
|
||||
<div
|
||||
className="event-card-header"
|
||||
style={{
|
||||
background: `url(${event.image}) no-repeat`,
|
||||
}}
|
||||
>
|
||||
<div className="event-card-title">
|
||||
{translate(event.name)}
|
||||
</div>
|
||||
<div
|
||||
className="event-card-header"
|
||||
style={{
|
||||
background: `url(${event.image}) no-repeat`,
|
||||
}}
|
||||
>
|
||||
<div className="event-card-title">
|
||||
{translate(event.name)}
|
||||
</div>
|
||||
<div className="event-card-body">
|
||||
<p>{translate(event.description)}</p>
|
||||
</div>
|
||||
<div className="mt-lg-auto event-card-footer d-flex flex-column">
|
||||
<span className="mb-2 d-flex icon icon-location">
|
||||
{event.location}
|
||||
</span>
|
||||
<span className="d-flex icon icon-date">{event.date}</span>
|
||||
</div>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
<div className="event-card-body">
|
||||
<p>{translate(event.description)}</p>
|
||||
</div>
|
||||
<div className="mt-lg-auto event-card-footer d-flex flex-column">
|
||||
<span className="mb-2 d-flex icon icon-location">
|
||||
{event.location}
|
||||
</span>
|
||||
<span className="d-flex icon icon-date">{event.date}</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
{/* Past Events */}
|
||||
<section className="container-new pt-26" id="past-events">
|
||||
<div className="p-0 pb-2 mb-4 d-flex flex-column-reverse col-lg-6 pr-lg-5">
|
||||
<h3 className="h4 h2-sm">
|
||||
{translate("Explore past community-hosted events")}
|
||||
</h3>
|
||||
<h6 className="mb-3 eyebrow">{translate("Past Events")}</h6>
|
||||
</div>
|
||||
<div className="filter row col-12 mt-lg-5 d-flex flex-column">
|
||||
<h6 className="mb-3">{translate("Filter By:")}</h6>
|
||||
<div>
|
||||
<div className="form-check form-check-inline">
|
||||
<input
|
||||
defaultValue="conference"
|
||||
id="conference-past"
|
||||
name="conference-past"
|
||||
type="checkbox"
|
||||
className="events-filter"
|
||||
checked={pastFilters.conference}
|
||||
onChange={handlePastFilterChange}
|
||||
/>
|
||||
<label htmlFor="conference-past">
|
||||
{translate("Conference")}
|
||||
</label>
|
||||
</div>
|
||||
<div className="form-check form-check-inline">
|
||||
<input
|
||||
defaultValue="meetup"
|
||||
id="meetup-past"
|
||||
name="meetup-past"
|
||||
type="checkbox"
|
||||
className="events-filter"
|
||||
checked={pastFilters.meetup}
|
||||
onChange={handlePastFilterChange}
|
||||
/>
|
||||
<label htmlFor="meetup-past">{translate("Meetups")}</label>
|
||||
</div>
|
||||
<div className="form-check form-check-inline">
|
||||
<input
|
||||
defaultValue="hackathon"
|
||||
id="hackathon-past"
|
||||
name="hackathon-past"
|
||||
type="checkbox"
|
||||
className="events-filter"
|
||||
checked={pastFilters.hackathon}
|
||||
onChange={handlePastFilterChange}
|
||||
/>
|
||||
<label htmlFor="hackathon-past">
|
||||
{translate("Hackathons")}
|
||||
</label>
|
||||
</div>
|
||||
<div className="form-check form-check-inline">
|
||||
<input
|
||||
defaultValue="ama"
|
||||
id="ama-past"
|
||||
name="ama-past"
|
||||
type="checkbox"
|
||||
className="events-filter"
|
||||
checked={pastFilters.ama}
|
||||
onChange={handlePastFilterChange}
|
||||
/>
|
||||
<label htmlFor="ama-past">{translate("AMAs")}</label>
|
||||
</div>
|
||||
<div className="form-check form-check-inline">
|
||||
<input
|
||||
defaultValue="cc"
|
||||
id="cc-past"
|
||||
name="cc-past"
|
||||
type="checkbox"
|
||||
className="events-filter"
|
||||
checked={pastFilters.cc}
|
||||
onChange={handlePastFilterChange}
|
||||
/>
|
||||
<label htmlFor="cc-past">{translate("Community Calls")}</label>
|
||||
</div>
|
||||
<div className="form-check form-check-inline">
|
||||
<input
|
||||
defaultValue="zone"
|
||||
id="zone-past"
|
||||
name="zone-past"
|
||||
type="checkbox"
|
||||
className="events-filter"
|
||||
checked={pastFilters.zone}
|
||||
onChange={handlePastFilterChange}
|
||||
/>
|
||||
<label htmlFor="zone-past">{translate("XRPL Zone")}</label>
|
||||
</div>
|
||||
<div className="form-check form-check-inline">
|
||||
<input
|
||||
defaultValue="info"
|
||||
id="info-past"
|
||||
name="info-past"
|
||||
type="checkbox"
|
||||
className="events-filter"
|
||||
checked={pastFilters["info"]}
|
||||
onChange={handlePastFilterChange}
|
||||
/>
|
||||
<label htmlFor="info-past">
|
||||
{translate("Info Session")}
|
||||
</label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
{/* Past Events */}
|
||||
<section className="container-new pt-26" id="past-events">
|
||||
<div className="p-0 pb-2 mb-4 d-flex flex-column-reverse col-lg-6 pr-lg-5">
|
||||
<h3 className="h4 h2-sm">
|
||||
{translate("Explore past community-hosted events")}
|
||||
</h3>
|
||||
<h6 className="mb-3 eyebrow">{translate("Past Events")}</h6>
|
||||
</div>
|
||||
<div className="filter row col-12 mt-lg-5 d-flex flex-column">
|
||||
<h6 className="mb-3">{translate("Filter By:")}</h6>
|
||||
<div>
|
||||
<div className="form-check form-check-inline">
|
||||
<input
|
||||
defaultValue="conference"
|
||||
id="conference-past"
|
||||
name="conference-past"
|
||||
type="checkbox"
|
||||
className="events-filter"
|
||||
checked={pastFilters.conference}
|
||||
onChange={handlePastFilterChange}
|
||||
/>
|
||||
<label htmlFor="conference-past">
|
||||
{translate("Conference")}
|
||||
</label>
|
||||
</div>
|
||||
<div className="form-check form-check-inline">
|
||||
<input
|
||||
defaultValue="meetup"
|
||||
id="meetup-past"
|
||||
name="meetup-past"
|
||||
type="checkbox"
|
||||
className="events-filter"
|
||||
checked={pastFilters.meetup}
|
||||
onChange={handlePastFilterChange}
|
||||
/>
|
||||
<label htmlFor="meetup-past">{translate("Meetups")}</label>
|
||||
</div>
|
||||
<div className="form-check form-check-inline">
|
||||
<input
|
||||
defaultValue="hackathon"
|
||||
id="hackathon-past"
|
||||
name="hackathon-past"
|
||||
type="checkbox"
|
||||
className="events-filter"
|
||||
checked={pastFilters.hackathon}
|
||||
onChange={handlePastFilterChange}
|
||||
/>
|
||||
<label htmlFor="hackathon-past">
|
||||
{translate("Hackathons")}
|
||||
</label>
|
||||
</div>
|
||||
<div className="form-check form-check-inline">
|
||||
<input
|
||||
defaultValue="ama"
|
||||
id="ama-past"
|
||||
name="ama-past"
|
||||
type="checkbox"
|
||||
className="events-filter"
|
||||
checked={pastFilters.ama}
|
||||
onChange={handlePastFilterChange}
|
||||
/>
|
||||
<label htmlFor="ama-past">{translate("AMAs")}</label>
|
||||
</div>
|
||||
<div className="form-check form-check-inline">
|
||||
<input
|
||||
defaultValue="cc"
|
||||
id="cc-past"
|
||||
name="cc-past"
|
||||
type="checkbox"
|
||||
className="events-filter"
|
||||
checked={pastFilters.cc}
|
||||
onChange={handlePastFilterChange}
|
||||
/>
|
||||
<label htmlFor="cc-past">{translate("Community Calls")}</label>
|
||||
</div>
|
||||
<div className="form-check form-check-inline">
|
||||
<input
|
||||
defaultValue="zone"
|
||||
id="zone-past"
|
||||
name="zone-past"
|
||||
type="checkbox"
|
||||
className="events-filter"
|
||||
checked={pastFilters.zone}
|
||||
onChange={handlePastFilterChange}
|
||||
/>
|
||||
<label htmlFor="zone-past">{translate("XRPL Zone")}</label>
|
||||
</div>
|
||||
<div className="form-check form-check-inline">
|
||||
<input
|
||||
defaultValue="info"
|
||||
id="info-past"
|
||||
name="info-past"
|
||||
type="checkbox"
|
||||
className="events-filter"
|
||||
checked={pastFilters["info"]}
|
||||
onChange={handlePastFilterChange}
|
||||
/>
|
||||
<label htmlFor="info-past">
|
||||
{translate("Info Session")}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-2 mb-0 row row-cols-1 row-cols-lg-3 card-deck ">
|
||||
{filteredPast.map((event, i) => (
|
||||
</div>
|
||||
<div className="row row-cols-1 row-cols-lg-3 g-4 mt-2 mb-0">
|
||||
{filteredPast.map((event, i) => (
|
||||
<div key={event.name + i} className="col">
|
||||
<a
|
||||
key={event.name + i}
|
||||
className="event-card {event.type}"
|
||||
className={`event-card ${event.type} h-100`}
|
||||
href={event.link}
|
||||
target="_blank"
|
||||
>
|
||||
@@ -1755,13 +1786,13 @@ export default function Events() {
|
||||
<span className="mb-2 d-flex icon icon-location">
|
||||
{event.location}
|
||||
</span>
|
||||
<span className="d-flex icon icon-date">{event.date}</span>
|
||||
</div>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<span className="d-flex icon icon-date">{event.date}</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
320
develop/index.page.tsx
Normal file
320
develop/index.page.tsx
Normal file
@@ -0,0 +1,320 @@
|
||||
import { useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
import { HeaderHeroSplitMedia } from "shared/sections/HeaderHeroSplitMedia/HeaderHeroSplitMedia";
|
||||
import { CardsFeatured } from "shared/sections/CardsFeatured/CardsFeatured";
|
||||
import { StandardCardGroupSection } from "shared/sections/StandardCardGroupSection/StandardCardGroupSection";
|
||||
import { LogoSquareGrid } from "shared/sections/LogoSquareGrid";
|
||||
import { FeatureTwoColumn } from "shared/sections/FeatureTwoColumn/FeatureTwoColumn";
|
||||
import { LinkTextDirectory } from "shared/sections/LinkTextDirectory/LinkTextDirectory";
|
||||
import { FeatureSingleTopic } from "shared/sections/FeatureSingleTopic/FeatureSingleTopic";
|
||||
|
||||
export const frontmatter = {
|
||||
seo: {
|
||||
title: "XRPL Developer Portal",
|
||||
description:
|
||||
"Build on XRPL: everything you need to go from idea to launch—documentation, SDKs, code samples, tutorials, and tools to build real-world apps on a decentralized, enterprise-grade ledger.",
|
||||
},
|
||||
};
|
||||
|
||||
export default function Develop() {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
return (
|
||||
<div className="landing">
|
||||
<HeaderHeroSplitMedia
|
||||
title={translate("XRPL Developer Portal")}
|
||||
subtitle={translate(
|
||||
"Build on XRPL: Your All-in-One Blockchain Developer Portal"
|
||||
)}
|
||||
description={translate(
|
||||
"Everything you need to go from idea to launch—documentation, SDKs, code samples, tutorials, and tools to build real-world apps on a decentralized, enterprise-grade ledger."
|
||||
)}
|
||||
primaryCta={{
|
||||
label: translate("Start Building"),
|
||||
href: "/docs",
|
||||
}}
|
||||
secondaryCta={{
|
||||
label: translate("Explore Tools"),
|
||||
href: "/resources/dev-tools",
|
||||
}}
|
||||
media={{
|
||||
src: require("../static/img/bds-2026/develop-hero-media.jpg"),
|
||||
alt: translate("XRPL Developer Portal"),
|
||||
}}
|
||||
/>
|
||||
|
||||
<CardsFeatured
|
||||
heading={translate("Learn the Fundamentals")}
|
||||
description={translate(
|
||||
"Start with the basics: clear concepts, in-depth tutorials, and comprehensive docs."
|
||||
)}
|
||||
cards={[
|
||||
{
|
||||
image: require("../static/img/bds-2026/develop-feature-media-1.jpg"),
|
||||
imageAlt: translate("Documentation"),
|
||||
title: translate("Documentation"),
|
||||
subtitle: translate(
|
||||
"Access everything you need to get started on the XRPL."
|
||||
),
|
||||
buttonLabel: translate("Documentation"),
|
||||
href: "/docs",
|
||||
},
|
||||
{
|
||||
image: require("../static/img/bds-2026/develop-feature-media-2.jpg"),
|
||||
imageAlt: translate("Guided Tutorials"),
|
||||
title: translate("Guided Tutorials"),
|
||||
subtitle: translate(
|
||||
"Follow step-by-step guides for frequent tasks."
|
||||
),
|
||||
buttonLabel: translate("Tutorials"),
|
||||
href: "/docs/tutorials",
|
||||
},
|
||||
{
|
||||
image: require("../static/img/bds-2026/develop-feature-media-3.jpg"),
|
||||
imageAlt: translate("Concepts"),
|
||||
title: translate("Concepts"),
|
||||
subtitle: translate(
|
||||
"Read about the XRPL's foundational concepts."
|
||||
),
|
||||
buttonLabel: translate("Concepts"),
|
||||
href: "/docs/concepts",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<StandardCardGroupSection
|
||||
headline={translate("Build with Our Tools")}
|
||||
description={translate(
|
||||
"Go from zero to code with SDKs, client libraries, and practical samples."
|
||||
)}
|
||||
variant="green"
|
||||
cards={[
|
||||
{
|
||||
headline: translate("SDKs & Libraries"),
|
||||
children: translate(
|
||||
"Access everything you need to get started on the XRPL."
|
||||
),
|
||||
callsToAction: [
|
||||
{
|
||||
children: translate("Access Here"),
|
||||
href: "/docs/references/client-libraries",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
headline: translate("Code Samples"),
|
||||
children: translate(
|
||||
"Browse sample code for building common use cases on the XRP Ledger"
|
||||
),
|
||||
callsToAction: [
|
||||
{
|
||||
children: translate("Access Here"),
|
||||
href: "/resources/code-samples",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
headline: translate("Developer Tools"),
|
||||
children: translate(
|
||||
"Use the developer tools to test, explore, and validate XRP Ledger API requests and behavior."
|
||||
),
|
||||
callsToAction: [
|
||||
{
|
||||
children: translate("Access Here"),
|
||||
href: "/resources/dev-tools",
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<LogoSquareGrid
|
||||
variant="gray"
|
||||
heading={translate("Apply to Real-World Use Cases")}
|
||||
description={translate(
|
||||
"See how XRPL powers tokenization, payments, credit markets, and beyond."
|
||||
)}
|
||||
buttons={[
|
||||
{
|
||||
label: translate("See What You Can Build"),
|
||||
href: "/about/uses",
|
||||
},
|
||||
]}
|
||||
logos={[
|
||||
{
|
||||
logo: require("../static/img/logos/black/archax.svg"),
|
||||
alt: translate("Archax"),
|
||||
},
|
||||
{
|
||||
logo: require("../static/img/logos/black/zoniqx.png"),
|
||||
alt: translate("Zoniqx"),
|
||||
},
|
||||
{
|
||||
logo: require("../static/img/logos/black/hidden-road.png"),
|
||||
alt: translate("Hidden Road"),
|
||||
},
|
||||
{
|
||||
logo: require("../static/img/logos/black/coinpayments.png"),
|
||||
alt: translate("Coinpayments"),
|
||||
},
|
||||
{
|
||||
logo: require("../static/img/logos/black/ripple.png"),
|
||||
alt: translate("Ripple"),
|
||||
},
|
||||
{
|
||||
logo: require("../static/img/logos/black/frii.png"),
|
||||
alt: translate("Frii"),
|
||||
},
|
||||
{
|
||||
logo: require("../static/img/logos/black/sologenic.png"),
|
||||
alt: translate("Sologenic"),
|
||||
},
|
||||
{
|
||||
logo: require("../static/img/logos/black/first-ledger.png"),
|
||||
alt: translate("First Ledger"),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<FeatureTwoColumn
|
||||
color="yellow"
|
||||
arrange="left"
|
||||
title={translate("XRPL Explorer")}
|
||||
description={translate(
|
||||
"Search, track, and understand XRP Ledger transactions through an open-source explorer."
|
||||
)}
|
||||
links={[
|
||||
{
|
||||
label: translate("XRPL Explorer"),
|
||||
href: "https://livenet.xrpl.org/",
|
||||
},
|
||||
]}
|
||||
media={{
|
||||
src: require("../static/img/bds-2026/develop-feature-media-4.jpg"),
|
||||
alt: translate("XRPL Explorer"),
|
||||
}}
|
||||
/>
|
||||
|
||||
<FeatureTwoColumn
|
||||
color="yellow"
|
||||
arrange="right"
|
||||
title={translate("Testnet Faucet")}
|
||||
description={translate(
|
||||
"Use testnets to trial XRPL updates and apps safely, without putting real funds at risk"
|
||||
)}
|
||||
links={[
|
||||
{
|
||||
label: translate("Testnet Faucet"),
|
||||
href: "/resources/dev-tools/xrp-faucets",
|
||||
},
|
||||
]}
|
||||
media={{
|
||||
src: require("../static/img/bds-2026/develop-feature-media-5.jpg"),
|
||||
alt: translate("Testnet Faucet"),
|
||||
}}
|
||||
/>
|
||||
|
||||
<FeatureTwoColumn
|
||||
color="yellow"
|
||||
arrange="left"
|
||||
title={translate("Simulate")}
|
||||
description={translate(
|
||||
"The simulate method executes a dry run of any transaction type, enabling you to preview the results and metadata of a transaction without committing them to the XRP Ledger."
|
||||
)}
|
||||
links={[
|
||||
{
|
||||
label: translate("Simulate"),
|
||||
href: "/docs/references/http-websocket-apis/public-api-methods/transaction-methods/simulate",
|
||||
},
|
||||
]}
|
||||
media={{
|
||||
src: require("../static/img/bds-2026/develop-feature-media-6.jpg"),
|
||||
alt: translate("Simulate"),
|
||||
}}
|
||||
/>
|
||||
|
||||
<LinkTextDirectory
|
||||
heading={translate("Developer Resources")}
|
||||
description={translate(
|
||||
"Stay connected with the latest releases, integrations, funding, and real-world blockchain developer stories."
|
||||
)}
|
||||
cards={[
|
||||
{
|
||||
heading: translate("Known Amendments"),
|
||||
description: translate(
|
||||
"Quick access to the latest XRPL release notes, amendments (XLS), and technical specifications."
|
||||
),
|
||||
buttons: [
|
||||
{
|
||||
label: translate("Access Here"),
|
||||
href: "/resources/known-amendments",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: translate("Integration Points"),
|
||||
description: translate(
|
||||
"Explore wallets, custodians, bridges, and other partner integrations that will aid you in building full end-to-end solutions."
|
||||
),
|
||||
buttons: [
|
||||
{
|
||||
label: translate("Explore Here"),
|
||||
href: "/about/uses",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: translate("Funding & Grants"),
|
||||
description: translate(
|
||||
"Information on XRPL Grants and funding opportunities for developers."
|
||||
),
|
||||
buttons: [
|
||||
{
|
||||
label: translate("Find Out Here"),
|
||||
href: "https://xrplgrants.org/",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: translate("XRPL Blog"),
|
||||
description: translate(
|
||||
"Blockchain developer-focused blogs such as Dev Reflections and case studies showcasing real-world XRPL builds."
|
||||
),
|
||||
buttons: [
|
||||
{
|
||||
label: translate("Read Here"),
|
||||
href: "/blog",
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<FeatureSingleTopic
|
||||
orientation="left"
|
||||
title={translate("Join the Community")}
|
||||
description={translate(
|
||||
"XRPL is open-source and community-driven. Run validators, contribute code, and shape the future."
|
||||
)}
|
||||
buttons={[
|
||||
{
|
||||
label: translate("Events"),
|
||||
href: "/community/events",
|
||||
},
|
||||
{
|
||||
label: translate("Contribute"),
|
||||
href: "https://github.com/XRPLF",
|
||||
},
|
||||
{
|
||||
label: translate("Join the Dev Forum"),
|
||||
href: "https://discord.com/invite/sfX3ERAMjH",
|
||||
},
|
||||
]}
|
||||
media={{
|
||||
src: require("../static/img/bds-2026/develop-feature-media-7.jpg"),
|
||||
alt: translate("Join the Community"),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,426 +1,480 @@
|
||||
import * as React from 'react';
|
||||
import { useThemeHooks } from '@redocly/theme/core/hooks';
|
||||
import { NavList } from "shared/components/nav-list";
|
||||
import { Link } from "@redocly/theme/components/Link/Link";
|
||||
import HeaderHeroPrimaryMedia from 'shared/sections/HeaderHeroPrimaryMedia/HeaderHeroPrimaryMedia';
|
||||
import { CarouselCardList } from 'shared/sections/CarouselCardList/CarouselCardList';
|
||||
import { CardsTextGrid } from 'shared/sections/CardsTextGrid/CardsTextGrid';
|
||||
import FeaturedVideoHero from 'shared/sections/FeaturedVideoHero/FeaturedVideoHero';
|
||||
import { LinkSmallGrid } from 'shared/sections/LinkSmallGrid/LinkSmallGrid';
|
||||
import { LinkTextDirectory } from 'shared/sections/LinkTextDirectory/LinkTextDirectory';
|
||||
import { FeatureTwoColumn } from 'shared/sections/FeatureTwoColumn/FeatureTwoColumn';
|
||||
import { SmallTilesSection } from 'shared/sections/SmallTilesSection/SmallTilesSection';
|
||||
import { CardsIconGrid } from 'shared/sections/CardsIconGrid/CardsIconGrid';
|
||||
import { StandardCardGroupSection } from 'shared/sections/StandardCardGroupSection/StandardCardGroupSection';
|
||||
|
||||
export const frontmatter = {
|
||||
seo: {
|
||||
title: 'XRP Ledger Documentation & Developer Resources',
|
||||
description: "Explore XRP Ledger documentation and other blockchain developer resources needed to start building and integrating with the ledger.",
|
||||
}
|
||||
description:
|
||||
'Explore XRP Ledger documentation and other blockchain developer resources needed to start building and integrating with the ledger.',
|
||||
},
|
||||
};
|
||||
|
||||
const recommendedPages = [
|
||||
{
|
||||
description: 'Public API Methods',
|
||||
link: '/docs/references/http-websocket-apis/public-api-methods/',
|
||||
},
|
||||
{
|
||||
description: 'Run a Validator',
|
||||
link: '/docs/infrastructure/configuration/server-modes/run-rippled-as-a-validator/',
|
||||
},
|
||||
{
|
||||
description: 'Reserves',
|
||||
link: '/docs/concepts/accounts/reserves/',
|
||||
},
|
||||
{
|
||||
description: 'Transaction Types',
|
||||
link: '/docs/references/protocol/transactions/types/',
|
||||
}
|
||||
];
|
||||
|
||||
const useCases = [
|
||||
{
|
||||
title: 'On-Chain Finance',
|
||||
id: 'on-chain-finance-use-cases',
|
||||
imgClass: 'wallet-illustration',
|
||||
subItems: [
|
||||
{
|
||||
description: 'Algorithmic Trading',
|
||||
link: '/docs/use-cases/defi/algorithmic-trading/',
|
||||
},
|
||||
{
|
||||
description: 'List XRP as an Exchange',
|
||||
link: '/docs/use-cases/defi/list-xrp-as-an-exchange/',
|
||||
},
|
||||
{
|
||||
description: 'Payment Types',
|
||||
link: '/docs/concepts/payment-types/',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Tokens',
|
||||
id: 'token-use-cases',
|
||||
imgClass: 'token-illustration',
|
||||
subItems: [
|
||||
{
|
||||
description: 'Stablecoin Issuer',
|
||||
link: '/docs/use-cases/tokenization/stablecoin-issuer/',
|
||||
},
|
||||
{
|
||||
description: 'NFT Marketplace',
|
||||
link: '/docs/use-cases/tokenization/nft-mkt-overview/',
|
||||
},
|
||||
{
|
||||
description: 'Digital Artist',
|
||||
link: '/docs/use-cases/tokenization/digital-artist/',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Payments',
|
||||
id: 'payments-use-cases',
|
||||
imgClass: 'connections-illustration',
|
||||
subItems: [
|
||||
{
|
||||
description: 'Peer-to-Peer Payments',
|
||||
link: '/docs/use-cases/payments/peer-to-peer-payments-uc/',
|
||||
},
|
||||
{
|
||||
description: 'Cross-Currency Payments',
|
||||
link: '/docs/concepts/payment-types/cross-currency-payments/',
|
||||
},
|
||||
{
|
||||
description: 'Smart Contracts',
|
||||
link: '/docs/use-cases/payments/smart-contracts-uc/',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
const intermediateVideos = [
|
||||
{
|
||||
src: require('../static/img/backgrounds/docs-advanced-payment-features@2x.png'),
|
||||
title: 'Advanced Payment Features',
|
||||
url: 'https://www.youtube.com/embed/e2Iwsk37LMk?rel=0&showinfo=0&autoplay=1',
|
||||
},
|
||||
{
|
||||
src: require('../static/img/backgrounds/docs-governance@2x.png'),
|
||||
title: 'Governance and the Amendment Process',
|
||||
url: 'https://www.youtube.com/embed/4GbRdanHoR4?rel=0&showinfo=0&autoplay=1',
|
||||
},
|
||||
{
|
||||
src: require('../static/img/backgrounds/docs-sidechains@2x.png'),
|
||||
title: 'Federated Sidechains',
|
||||
url: 'https://www.youtube.com/embed/NhH4LM8NxgY?rel=0&showinfo=0&autoplay=1',
|
||||
},
|
||||
];
|
||||
const getStartedVideos = [
|
||||
{
|
||||
src: require('../static/img/backgrounds/docs-intro-to-XRP-ledger@2x.png'),
|
||||
title: 'Intro to XRP Ledger',
|
||||
url: 'https://www.youtube.com/embed/sVTybJ3cNyo?rel=0&showinfo=0&autoplay=1',
|
||||
},
|
||||
{
|
||||
src: require('../static/img/backgrounds/docs-accounts@2x.png'),
|
||||
title: 'Accounts',
|
||||
url: 'https://www.youtube.com/embed/eO8jE6PftX8?rel=0&showinfo=0&autoplay=1',
|
||||
},
|
||||
{
|
||||
src: require('../static/img/backgrounds/docs-decentralized-exchange@2x.png'),
|
||||
title: 'Decentralized Exchange',
|
||||
url: 'https://www.youtube.com/embed/VWNrHBDfXvA?rel=0&showinfo=0&autoplay=1',
|
||||
},
|
||||
{
|
||||
src: require('../static/img/backgrounds/docs-tokenization@2x.png'),
|
||||
title: 'Tokenization',
|
||||
url: 'https://www.youtube.com/embed/Oj4cWOiWf4A?rel=0&showinfo=0&autoplay=1',
|
||||
},
|
||||
];
|
||||
|
||||
const devTools = [
|
||||
{
|
||||
title: 'XRP Faucets',
|
||||
link: '/resources/dev-tools/xrp-faucets',
|
||||
description: 'Get credentials and test-XRP for XRP Ledger Testnet or Devnet.',
|
||||
},
|
||||
{
|
||||
title: 'WebSocket Tool',
|
||||
link: '/resources/dev-tools/websocket-api-tool',
|
||||
description: 'Send sample requests and get responses from the rippled API.',
|
||||
},
|
||||
{
|
||||
title: 'XRP Ledger Explorer',
|
||||
link: 'https://livenet.xrpl.org',
|
||||
description:
|
||||
'View validations of new ledger versions in real-time, chart the location of servers in the XRP Ledger.',
|
||||
},
|
||||
{
|
||||
title: 'Transaction Sender',
|
||||
link: '/resources/dev-tools/tx-sender',
|
||||
description:
|
||||
'Test how your code handles various XRP Ledger transactions by sending them over the Testnet to the address.',
|
||||
},
|
||||
];
|
||||
|
||||
function UseCasesCard(props: {
|
||||
useCase: {
|
||||
id: string;
|
||||
title: string;
|
||||
imgClass: string;
|
||||
subItems: { description: string; link: string }[];
|
||||
};
|
||||
}) {
|
||||
const { useCase } = props;
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
return (
|
||||
<div className="col">
|
||||
<img className={'use-cases-img img-fluid mb-2 shadow ' + useCase.imgClass} alt={useCase.title} id={useCase.id} />
|
||||
<h5 className="mt-4">{translate(useCase.title)}</h5>
|
||||
<NavList pages={useCase.subItems} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function FlatCard(props: { href: string; title: string; description: string; linkText: string; imgClass }) {
|
||||
const { title, description, linkText, href, imgClass } = props;
|
||||
return (
|
||||
<Link to={href} className="card flat-card float-up-on-hover">
|
||||
<img className={'mb-2 ' + imgClass} alt={title} />
|
||||
<h5 className="row">
|
||||
<div className="nav-link">{title}</div>
|
||||
</h5>
|
||||
<p className="row faded-text flat-card-padding">{description}</p>
|
||||
<div className="col align-button-on-bottom">
|
||||
<div className="btn btn-primary btn-arrow" id={href + '-button'}>
|
||||
{linkText}
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
function VideoCard(props: { url: string; title: string; src: string }) {
|
||||
const { url, title, src } = props;
|
||||
return (
|
||||
<div className="col float-up-on-hover">
|
||||
<a href={url} id="playvideo" className="btn1" data-url={url}>
|
||||
<img alt={title} className="get-started-img video-image" id={title} src={src} />
|
||||
<h6 className="pt-3">{title}</h6>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function DevToolCard(props: { link: string; title: string; description: string }) {
|
||||
const { link, title, description } = props;
|
||||
return (
|
||||
<Link to={link} className="col dev-tools-link">
|
||||
<h6 className="btn-arrow">{title}</h6>
|
||||
<p> {description}</p>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
function PrimaryButton(props: { href: string; text: string; isArrowUp: boolean }) {
|
||||
const { href, text, isArrowUp } = props;
|
||||
return (
|
||||
<Link className={`btn btn-primary ${isArrowUp ? 'btn-arrow-out' : 'btn-arrow'}`} id={href + '-button'} to={href}>
|
||||
{text}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Docs() {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
return (
|
||||
<div className="landing page-docs page-docs-index landing-builtin-bg overflow-hidden styled-page">
|
||||
<div>
|
||||
<section className="text-center title-space">
|
||||
<div className="col-lg-9 mx-auto text-center">
|
||||
<div className="d-flex flex-column-reverse">
|
||||
<h1>{translate('Documentation')}</h1>
|
||||
<h6 className="eyebrow mb-3">{translate('XRP Ledger Developer Resources')}</h6>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div className="landing page-docs page-docs-index">
|
||||
|
||||
<section className="container-new ">
|
||||
<div className="nav card-grid flat-card-grid card-grid-3xN">
|
||||
<div className="col">
|
||||
<FlatCard
|
||||
href="/docs/concepts/"
|
||||
title={translate('Concepts')}
|
||||
description={translate('Learn the "what" and the "why" behind fundamental aspects of the XRP Ledger.')}
|
||||
linkText={translate('Read the Docs')}
|
||||
imgClass="concepts-doc-illustration"
|
||||
/>
|
||||
</div>
|
||||
<div className="col">
|
||||
<FlatCard
|
||||
href="/docs/tutorials/"
|
||||
title={translate('Tutorials')}
|
||||
description={translate('Get step-by-step guidance to perform common tasks with the XRP Ledger.')}
|
||||
linkText={translate('View Tutorials')}
|
||||
imgClass="tutorial-illustration"
|
||||
/>
|
||||
</div>
|
||||
<div className="col">
|
||||
<FlatCard
|
||||
href="/docs/references/"
|
||||
title={translate('References')}
|
||||
description={translate(
|
||||
'Look up reference documentation for the XRP Ledger protocol, API methods, and more.'
|
||||
)}
|
||||
linkText={translate('View References')}
|
||||
imgClass="ref-book-illustration"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section className="container-new">
|
||||
<h4 className="pb-4">{translate('Use Cases')}</h4>
|
||||
<div className="card-grid card-grid-3xN use-cases">
|
||||
{useCases.map(useCase => (
|
||||
<UseCasesCard useCase={useCase} key={useCase.id} />
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
<section className="container-new ">
|
||||
<h4 className="pb-4">{translate('Getting Started')}</h4>
|
||||
<div className="card-grid card-grid-2xN quickstart-card">
|
||||
<div className="col">
|
||||
<Link to="/docs/introduction/" className="card float-up-on-hover">
|
||||
<h5 className="mt-7">{translate('Introduction to the XRP Ledger')}</h5>
|
||||
<p className="mb-8 mt-4">{translate('An introduction to fundamental aspects of the XRP Ledger.')}</p>
|
||||
<div className="dg-lg-block mb-3">
|
||||
<div className="btn btn-primary btn-arrow get-started-button">{translate('Introduction')}</div>
|
||||
</div>
|
||||
<img alt="quick-start" id="quick-start-img" className="quickstart-image" />
|
||||
</Link>
|
||||
</div>
|
||||
<div className="col">
|
||||
<div className="card-grid card-grid-2xN video-grid">
|
||||
{getStartedVideos.map(video => (
|
||||
<VideoCard url={video.url} title={translate(video.title)} src={video.src} key={video.url} />
|
||||
))}
|
||||
</div>
|
||||
<div className="align-button-on-bottom">
|
||||
<PrimaryButton
|
||||
href="https://www.youtube.com/playlist?list=PLJQ55Tj1hIVZtJ_JdTvSum2qMTsedWkNi"
|
||||
text={translate('Watch Full Series')}
|
||||
isArrowUp={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section className="container-new ">
|
||||
<div className="d-flex flex-column-reverse col-sm-8 p-0">
|
||||
<h3 className="h4 h2-sm">{translate('Interact with the XRP Ledger in a language of your choice')}</h3>
|
||||
<h6 className="eyebrow mb-3">{translate('Explore SDKs')}</h6>
|
||||
</div>
|
||||
<div className="card-grid card-grid-2xN">
|
||||
<div className="col">
|
||||
<div className="card-grid langs-cards card-grid-2xN mt-10" id="langs-cards">
|
||||
<div className="col langs">
|
||||
<Link to="/docs/tutorials/javascript/">
|
||||
<img alt="Javascript Logo" src={require('../static/img/logos/javascript.svg')} className="circled-logo" />
|
||||
<h5 className="btn-arrow">{translate('Javascript')}</h5>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="col langs">
|
||||
<Link to="/docs/tutorials/python/">
|
||||
<img alt="Python Logo" src={require('../static/img/logos/python.svg')} className="circled-logo" />
|
||||
<h5 className="btn-arrow">{translate('Python')}</h5>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="col langs">
|
||||
<Link to="/docs/tutorials/java/build-apps/get-started/">
|
||||
<img alt="Java Logo" src={require('../static/img/logos/java.svg')} className="circled-logo" />
|
||||
<h5 className="btn-arrow">{translate('Java')}</h5>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="col langs">
|
||||
<Link to="/docs/tutorials/go/">
|
||||
<img
|
||||
alt="Go Logo"
|
||||
src={require("../static/img/logos/golang.svg")}
|
||||
className="circled-logo"
|
||||
/>
|
||||
<h5 className="btn-arrow">{translate("GoLang")}</h5>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col center-image">
|
||||
<img className="img-fluid sdk-img" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section className="container-new ">
|
||||
<h4 className="pb-4">{translate('Intermediate Learning Sources')}</h4>
|
||||
<div className="card-grid card-grid-3xN">
|
||||
{intermediateVideos.map(video => (
|
||||
<VideoCard url={video.url} title={translate(video.title)} src={video.src} key={video.url} />
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
<section className="container-new ">
|
||||
<div className="card-grid card-grid-2xN">
|
||||
<div className="col d-flex align-items-center justify-content-center">
|
||||
<img className="dev-tools-img" />
|
||||
</div>
|
||||
<div className="col explore-links">
|
||||
<div className="d-flex flex-column-reverse w-100">
|
||||
<h4 className="mb-10">{translate('Explore, Test, Verify')}</h4>
|
||||
<h6 className="mb-3">{translate('Explore Dev Tools')}</h6>
|
||||
</div>
|
||||
<p className="mb-20">
|
||||
{translate(
|
||||
'Use these web-based tools to assist during all stages of development, from getting your first payment to testing your implementation for best practices.'
|
||||
)}
|
||||
</p>
|
||||
<div className="card-grid card-grid-2xN">
|
||||
{devTools.map(card => (
|
||||
<DevToolCard
|
||||
link={card.link}
|
||||
title={translate(card.title)}
|
||||
description={translate(card.description)}
|
||||
key={card.link}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<PrimaryButton href="/resources/dev-tools" text={translate('View All tools')} isArrowUp={false} />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section className="container-new " id="docs-browse-by">
|
||||
<div className="row card-grid card-grid-2xN">
|
||||
<div className="col" id="popular-topics">
|
||||
<h2 className="h4">{translate('Browse By Recommended Pages')}</h2>
|
||||
<NavList pages={recommendedPages} />
|
||||
</div>
|
||||
<div className="col">
|
||||
<div className="card cta-card p-8-sm p-10-until-sm br-8">
|
||||
<img src={require('../static/img/backgrounds/cta-home-purple.svg')} className="d-none-sm cta cta-top-left" />
|
||||
<img src={require('../static/img/backgrounds/cta-home-green.svg')} className="cta cta-bottom-right" />
|
||||
<div className="z-index-1 position-relative">
|
||||
<h2 className="h4 mb-8-sm mb-10-until-sm">{translate('Get Free Test XRP')}</h2>
|
||||
<p className="mb-10">
|
||||
{translate(
|
||||
'Connect to the XRP Ledger Testnet network to develop and test your apps built on the XRP Ledger, without risking real money or impacting production XRP Ledger users.'
|
||||
)}
|
||||
</p>
|
||||
<Link className="btn btn-primary btn-arrow" to="/resources/dev-tools/xrp-faucets/">
|
||||
{translate('Generate Testnet Credentials')}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{/* full docs index isn't ported to Redocly
|
||||
<section className="container-new">
|
||||
<a href="/docs/full-index" className="btn-arrow arrow-purple documentation-index mr-auto">
|
||||
{translate('See full documentation index')}
|
||||
</a>
|
||||
</section>
|
||||
*/}
|
||||
</div>
|
||||
{/* 1. Hero */}
|
||||
<HeaderHeroPrimaryMedia
|
||||
headline={translate("XRP Ledger (XRPL) Documentation")}
|
||||
subtitle={translate("Explore XRPL documentation with our essential guide for developers and admins who want to start building and integrating with the XRP Ledger.")}
|
||||
media={{ type: 'image', src: require('../static/img/bds-2026/docs-hero-media.jpg'), alt: translate('XRPL Documentation') }}
|
||||
/>
|
||||
|
||||
{/* 2. Get Started Carousel */}
|
||||
<CarouselCardList
|
||||
variant="green"
|
||||
buttonVariant="green"
|
||||
heading={translate("Get Started: XRPL Developer & Admin Resources")}
|
||||
description={translate("Learn your way. Read docs, watch videos, or get hands-on with code samples. Explore different ways to learn on the XRP Ledger.")}
|
||||
cards={[
|
||||
{
|
||||
icon: require('../static/img/icons/2026/black/Ready-to-Use-Code-Samples.svg'),
|
||||
title: translate('Ready-to-Use Code Samples'),
|
||||
description: translate(
|
||||
'Run complete code snippets to understand XRPL integration in seconds.'
|
||||
),
|
||||
href: '/docs/tutorials/',
|
||||
},
|
||||
{
|
||||
icon: require('../static/img/icons/2026/black/Launch-Your-First-Project.svg'),
|
||||
title: translate('Launch Your First Project'),
|
||||
description: translate(
|
||||
'Explore funding and development opportunities for your project on the XRPL.'
|
||||
),
|
||||
href: '/resources/grant-funding/',
|
||||
},
|
||||
{
|
||||
icon: require('../static/img/icons/2026/black/Step-by-Step-Tutorials.svg'),
|
||||
title: translate('Step-by-Step Tutorials'),
|
||||
description: translate(
|
||||
'Follow guided walkthroughs to master XRPL fundamentals and industry best practices.'
|
||||
),
|
||||
href: '/docs/tutorials/',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 3. Core Concepts Text Grid */}
|
||||
<CardsTextGrid
|
||||
heading={translate("Dig Into Core Concepts & Tools")}
|
||||
cards={[
|
||||
{
|
||||
heading: translate('XRPL Fundamentals'),
|
||||
description: (
|
||||
<>
|
||||
{translate("Discover the basics of the XRPL by learning about accounts, transactions, and the ledger structure.")}{' '}<br/><br/>
|
||||
<a href="/docs/concepts/">{translate("Read More")}</a>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
heading: translate('Advanced XRPL Topics: Go Deeper'),
|
||||
description: (
|
||||
<>
|
||||
{translate("Implement real-world solutions by combining XRPL primitives for lending, token issuance, DEX trading, and more.")}{' '}<br/><br/>
|
||||
<a href="/docs/use-cases/">{translate("Read More")}</a>
|
||||
</>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 4. Featured Video — Advanced Payment Features */}
|
||||
<FeaturedVideoHero
|
||||
headline={translate("Advanced Payment Features")}
|
||||
subtitle={translate("Master sophisticated movement of value through features such as escrows, checks, or payment channels.")}
|
||||
video={{
|
||||
source: {
|
||||
type: 'embed',
|
||||
embedUrl: 'https://www.youtube.com/embed/e2Iwsk37LMk',
|
||||
},
|
||||
}}
|
||||
links={[
|
||||
{
|
||||
label: translate('Watch Now'),
|
||||
href: 'https://youtube.com/playlist?list=PLJQ55Tj1hIVZtJ_JdTvSum2qMTsedWkNi',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 5. Featured Video — Governance */}
|
||||
<FeaturedVideoHero
|
||||
headline={translate("Governance and the Amendment Process")}
|
||||
subtitle={translate("Understand how the decentralized network evolves through validator voting and how new features are activated.")}
|
||||
video={{
|
||||
source: {
|
||||
type: 'embed',
|
||||
embedUrl: 'https://www.youtube.com/embed/4GbRdanHoR4',
|
||||
},
|
||||
}}
|
||||
links={[
|
||||
{
|
||||
label: translate('Watch Now'),
|
||||
href: 'https://youtube.com/playlist?list=PLJQ55Tj1hIVZtJ_JdTvSum2qMTsedWkNi',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 6. Developer Reference Links */}
|
||||
<LinkSmallGrid
|
||||
variant="gray"
|
||||
heading={translate("Developers")}
|
||||
description={translate("Master the Protocol: Essential References")}
|
||||
links={[
|
||||
{
|
||||
label: translate('Transaction Types'),
|
||||
href: '/docs/references/protocol/transactions/types',
|
||||
},
|
||||
{
|
||||
label: translate('Account Methods'),
|
||||
href: '/docs/references/http-websocket-apis/public-api-methods/account-methods',
|
||||
},
|
||||
{
|
||||
label: translate('Ledger Entry Types'),
|
||||
href: '/docs/references/protocol/ledger-data/ledger-entry-types',
|
||||
},
|
||||
{
|
||||
label: translate('Transaction Methods'),
|
||||
href: '/docs/references/http-websocket-apis/public-api-methods/transaction-methods',
|
||||
},
|
||||
{
|
||||
label: translate('Basic Data Types'),
|
||||
href: '/docs/references/protocol/data-types/basic-data-types',
|
||||
},
|
||||
{
|
||||
label: translate('Path and Orderbook Methods'),
|
||||
href: '/docs/references/http-websocket-apis/public-api-methods/path-and-order-book-methods',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 7. Server Admin Reference Links */}
|
||||
<LinkSmallGrid
|
||||
variant="gray"
|
||||
heading={translate("Server Admins")}
|
||||
description={translate("Master the Protocol: Essential References")}
|
||||
links={[
|
||||
{
|
||||
label: translate('Commandline Usage'),
|
||||
href: '/docs/infrastructure/commandline-usage',
|
||||
},
|
||||
{
|
||||
label: translate('Admin API Methods'),
|
||||
href: '/docs/references/http-websocket-apis/admin-api-methods',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 8. Dev Tools Directory */}
|
||||
<LinkTextDirectory
|
||||
heading={translate("Tools to Test & Deploy")}
|
||||
cards={[
|
||||
{
|
||||
heading: translate('Get Test XRP (Faucets)'),
|
||||
description: translate(
|
||||
'Get credentials and test-XRP for XRP Ledger Testnet or Devnet.'
|
||||
),
|
||||
buttons: [
|
||||
{ label: translate('Access Here'), href: '/resources/dev-tools/xrp-faucets' },
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: translate('Send Test Transactions'),
|
||||
description: translate(
|
||||
'Test how your code handles various XRP Ledger transactions by sending them over the Testnet to the address.'
|
||||
),
|
||||
buttons: [
|
||||
{ label: translate('Access Here'), href: '/resources/dev-tools/tx-sender' },
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: translate('Explore WebSocket API'),
|
||||
description: translate(
|
||||
'Send sample requests and get responses from the rippled API.'
|
||||
),
|
||||
buttons: [
|
||||
{
|
||||
label: translate('Access Here'),
|
||||
href: '/resources/dev-tools/websocket-api-tool',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: translate('Monitor the XRP Ledger'),
|
||||
description: translate(
|
||||
'View validations of new ledger versions in real-time, chart the location of servers in the XRP Ledger.'
|
||||
),
|
||||
buttons: [
|
||||
{ label: translate('Access Here'), href: 'https://livenet.xrpl.org/' },
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 9a. Use Cases — Payments */}
|
||||
<FeatureTwoColumn
|
||||
color="lilac"
|
||||
arrange="left"
|
||||
title={translate("Payments")}
|
||||
description=""
|
||||
media={{ src: require('../static/img/bds-2026/docs-feature-media-1.jpg'), alt: translate('Payments') }}
|
||||
links={[
|
||||
{
|
||||
label: translate('Peer-to-Peer Payments'),
|
||||
href: '/docs/use-cases/payments/peer-to-peer-payments-uc',
|
||||
},
|
||||
{
|
||||
label: translate('Cross-Currency Payments'),
|
||||
href: '/docs/concepts/payment-types/cross-currency-payments',
|
||||
},
|
||||
{
|
||||
label: translate('Smart Contracts'),
|
||||
href: '/docs/use-cases/payments/smart-contracts-uc',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 9b. Use Cases — Tokens */}
|
||||
<FeatureTwoColumn
|
||||
color="lilac"
|
||||
arrange="right"
|
||||
title={translate("Tokens")}
|
||||
description=""
|
||||
media={{ src: require('../static/img/bds-2026/docs-feature-media-2.jpg'), alt: translate('Tokens') }}
|
||||
links={[
|
||||
{
|
||||
label: translate('Stablecoin Issuer'),
|
||||
href: '/docs/use-cases/tokenization/stablecoin-issuer',
|
||||
},
|
||||
{
|
||||
label: translate('NFT Marketplace'),
|
||||
href: '/docs/use-cases/tokenization/nft-mkt-overview',
|
||||
},
|
||||
{
|
||||
label: translate('Digital Artist'),
|
||||
href: '/docs/use-cases/tokenization/digital-artist',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 9c. Use Cases — On-Chain Finance */}
|
||||
<FeatureTwoColumn
|
||||
color="lilac"
|
||||
arrange="left"
|
||||
title={translate("On-Chain Finance")}
|
||||
description=""
|
||||
media={{ src: require('../static/img/bds-2026/docs-feature-media-3.jpg'), alt: translate('On-Chain Finance') }}
|
||||
links={[
|
||||
{
|
||||
label: translate('List XRP as an Exchange'),
|
||||
href: '/docs/use-cases/defi/list-xrp-as-an-exchange',
|
||||
},
|
||||
{
|
||||
label: translate('Trade with an Auction Slot'),
|
||||
href: '/docs/tutorials/javascript/amm/trade-with-auction-slot',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 9d. Use Cases — Compliance Features */}
|
||||
<FeatureTwoColumn
|
||||
color="lilac"
|
||||
arrange="right"
|
||||
title={translate("Compliance Features")}
|
||||
description=""
|
||||
media={{ src: require('../static/img/bds-2026/docs-feature-media.jpg'), alt: translate('Compliance Features') }}
|
||||
links={[
|
||||
{
|
||||
label: translate('Build a Credential Issuing Service'),
|
||||
href: '/docs/tutorials/javascript/build-apps/credential-issuing-service',
|
||||
},
|
||||
{
|
||||
label: translate('Create a Permissioned Domain'),
|
||||
href: '/docs/tutorials/javascript/compliance/create-permissioned-domains',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 10. SDK Tiles */}
|
||||
<SmallTilesSection
|
||||
headline={translate("Build with SDKs")}
|
||||
cardVariant="neutral"
|
||||
cards={[
|
||||
{
|
||||
icon: require('../static/img/logos/black/js.svg'),
|
||||
iconAlt: translate('JavaScript'),
|
||||
label: translate('Get Started with Javascript'),
|
||||
href: '/docs/tutorials/javascript',
|
||||
},
|
||||
{
|
||||
icon: require('../static/img/logos/black/python.svg'),
|
||||
iconAlt: translate('Python'),
|
||||
label: translate('Python'),
|
||||
href: '/docs/tutorials/python',
|
||||
},
|
||||
{
|
||||
icon: require('../static/img/logos/black/java.svg'),
|
||||
iconAlt: translate('Java'),
|
||||
label: translate('Java'),
|
||||
href: '/docs/tutorials/java/build-apps/get-started',
|
||||
},
|
||||
{
|
||||
icon: require('../static/img/logos/black/go.svg'),
|
||||
iconAlt: translate('Go'),
|
||||
label: translate('Go'),
|
||||
href: '/docs/tutorials/go',
|
||||
},
|
||||
{
|
||||
icon: require('../static/img/logos/black/php.svg'),
|
||||
iconAlt: translate('PHP'),
|
||||
label: translate('PHP'),
|
||||
href: '/docs/tutorials/php',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 11. Infrastructure Cards */}
|
||||
<CardsIconGrid
|
||||
heading={translate("XRPL Infrastructure: Running a Server")}
|
||||
cards={[
|
||||
{
|
||||
icon: require('../static/img/icons/2026/color/lilac/xrpl-server.svg'),
|
||||
iconAlt: translate('Server'),
|
||||
heading: translate('Install Your XRPL Server: Rippled & Clio'),
|
||||
description: (
|
||||
<>
|
||||
{translate("Take ownership of your connection to the blockchain with a core server that can submit transactions, read balances, and store a complete copy of the ledger data.")}{' '}
|
||||
<a href="/docs/infrastructure/installation">{translate("Learn More")}</a>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
icon: require('../static/img/icons/2026/color/lilac/node-configuration.svg'),
|
||||
iconAlt: translate('Network'),
|
||||
heading: translate('Node Configuration'),
|
||||
description: (
|
||||
<>
|
||||
{translate("Customize your server configuration for your use case, including data retention, network connectivity, and performance tuning.")}{' '}
|
||||
<a href="/docs/infrastructure/configuration">{translate("Learn More")}</a>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
icon: require('../static/img/icons/2026/color/lilac/troubleshooting-node.svg'),
|
||||
iconAlt: translate('Tools'),
|
||||
heading: translate('Troubleshooting Your Node'),
|
||||
description: (
|
||||
<>
|
||||
{translate("Diagnose and solve problems with your server to maximize uptime and reliability.")}{' '}
|
||||
<a href="/docs/infrastructure/troubleshooting">{translate("Learn More")}</a>
|
||||
</>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 12. Contribute Cards */}
|
||||
<StandardCardGroupSection
|
||||
headline={translate("Get Involved: Contribute to the XRP Ledger")}
|
||||
description=""
|
||||
variant="neutral"
|
||||
cards={[
|
||||
{
|
||||
headline: translate('Contribute to the Protocol'),
|
||||
callsToAction: [
|
||||
{ children: translate('Contribute Now'), href: '/resources/contribute-code' },
|
||||
],
|
||||
children: translate(
|
||||
'Have you identified gaps, edge cases, or improvements through real-world use? Contribute code or proposals and help shape the future of XRPL at the protocol level.'
|
||||
),
|
||||
},
|
||||
{
|
||||
headline: translate('Contribute to Docs'),
|
||||
callsToAction: [
|
||||
{
|
||||
children: translate('Contribute Now'),
|
||||
href: '/resources/contribute-documentation',
|
||||
},
|
||||
],
|
||||
children: (
|
||||
<>
|
||||
{translate("Contribute to")}{' '}
|
||||
<a href="https://xrpl.org/" target="_blank" rel="noreferrer">
|
||||
{translate("XRPL.org")}
|
||||
</a>
|
||||
{translate(", the go-to resource for all things XRP Ledger.")}
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
headline: translate('Contribute a Blog Post'),
|
||||
callsToAction: [
|
||||
{
|
||||
children: translate('Contribute Now'),
|
||||
href: '/resources/contribute-blog',
|
||||
},
|
||||
],
|
||||
children: translate(
|
||||
'Share how you solved a real-world problem using the XRP Ledger, including your architecture decisions, workflows, tradeoffs, and lessons learned. Contribute a blog post to help other developers build faster.'
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 13. Continuous Learning Cards */}
|
||||
<StandardCardGroupSection
|
||||
headline={translate("Continuous Learning: Additional XRPL Resources")}
|
||||
description=""
|
||||
variant="neutral"
|
||||
cards={[
|
||||
{
|
||||
headline: translate('XRPL Learning Portal'),
|
||||
callsToAction: [
|
||||
{ children: translate('Learn More'), href: 'https://learn.xrpl.org/' },
|
||||
],
|
||||
children: translate(
|
||||
'From blockchain fundamentals to building on the XRPL, create your own learning journey and progress at your own pace.'
|
||||
),
|
||||
},
|
||||
{
|
||||
headline: translate('Aquarium Residency Program'),
|
||||
callsToAction: [
|
||||
{
|
||||
children: translate('Learn More'),
|
||||
href: 'https://www.xrpl-commons.org/residency',
|
||||
},
|
||||
],
|
||||
children: translate(
|
||||
'A hands-on residency program helping teams build, test, and launch real-world solutions on the XRP Ledger.'
|
||||
),
|
||||
},
|
||||
{
|
||||
headline: translate('Developer Community Forum'),
|
||||
callsToAction: [
|
||||
{
|
||||
children: translate('Learn More'),
|
||||
href: 'https://discord.gg/sfX3ERAMjH',
|
||||
},
|
||||
],
|
||||
children: translate(
|
||||
'Join the XRPL developer community on Discord! Ask questions, share tips, and collaborate with builders turning ideas into real-world projects.'
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1106,7 +1106,7 @@ const { sendXrp } = require('./library/7_helpers')
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="send-xrp-modal-label">Send XRP</h1>
|
||||
<h1 class="modal-title subhead-sm-r" id="send-xrp-modal-label">Send XRP</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
@@ -1,320 +1,36 @@
|
||||
import React, { useState } from "react";
|
||||
import { useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
import { AdvantagesSection } from "shared/components/advantages-section";
|
||||
import { ProjectCards } from "shared/components/project-cards";
|
||||
import { BenefitsSection } from "shared/components/benefits-section";
|
||||
import { DeveloperResourcesSection } from "shared/components/developer-resources-section";
|
||||
import { FeatureItem } from "../tokenization/real-world-assets.page";
|
||||
import React from 'react';
|
||||
import {
|
||||
HeroSection,
|
||||
WhyChooseSection,
|
||||
AdvancedFeaturesSection,
|
||||
StablecoinsSection,
|
||||
EmbeddedPaymentsSection,
|
||||
PartnerLogosSection,
|
||||
FlexibleIntegrationSection,
|
||||
DeveloperSpotlightSection,
|
||||
StayConnectedSection,
|
||||
} from 'pages/payments/sections';
|
||||
|
||||
export const frontmatter = {
|
||||
seo: {
|
||||
title: 'XRP Ledger Payments Suite',
|
||||
description: "The XRP Ledger Payments Suite is a payments solution for use cases including stablecoin payments, cross-border remittance, B2B payment rails, and merchant settlement.",
|
||||
}
|
||||
title: 'Payments Infrastructure',
|
||||
description:
|
||||
'The XRP Ledger Payments Infrastructure is a payments solution for use cases including stablecoin payments, cross-border remittance, B2B payment rails, and merchant settlement.',
|
||||
},
|
||||
};
|
||||
|
||||
const PaymentsPage: React.FC = () => {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
const video = {
|
||||
url: "https://www.youtube.com/embed/e2Iwsk37LMk?si=20-m6aQOWpaiQDW7",
|
||||
title: "Payments",
|
||||
src: "https://www.youtube.com/embed/e2Iwsk37LMk?si=20-m6aQOWpaiQDW7",
|
||||
};
|
||||
|
||||
const paymentAdvantages = [
|
||||
{
|
||||
id: "cross-border-stablecoin",
|
||||
title: "Enable Cross-Border Stablecoin Payments",
|
||||
contents: [
|
||||
{
|
||||
subtitle: "RLUSD and USDC support",
|
||||
description: "",
|
||||
},
|
||||
{
|
||||
subtitle: "Easily receive, store, convert, issue and send stablecoins",
|
||||
description: "",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "reliable-infrastructure",
|
||||
title: "Access Reliable Payments Infrastructure",
|
||||
contents: [
|
||||
{
|
||||
subtitle: "99.99% uptime since 2012",
|
||||
description: "",
|
||||
},
|
||||
{
|
||||
subtitle: "Over $1.7T+ in value transferred",
|
||||
description: "",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "efficient-payments",
|
||||
title: "Move Money Efficiently",
|
||||
contents: [
|
||||
{
|
||||
subtitle: "Transactions settle in 3-5 seconds",
|
||||
description: "",
|
||||
},
|
||||
{
|
||||
subtitle: "Fractions of a cent per transaction",
|
||||
description: "",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const paymentProjects = [
|
||||
{
|
||||
id: "ripple-usd",
|
||||
label: "Ripple USD",
|
||||
url: "https://ripple.com/solutions/stablecoin/",
|
||||
description: "RLUSD, Ripple's enterprise-grade stablecoin, is live on XRPL and fully backed by USD deposits.",
|
||||
},
|
||||
{
|
||||
id: "usdc",
|
||||
label: "USDC",
|
||||
url: "https://www.circle.com/usdc",
|
||||
description: "USDC, issued by Circle, is the world's largest regulated dollar stablecoin and now live on XRPL.",
|
||||
},
|
||||
{
|
||||
id: "usdb",
|
||||
label: "USDB",
|
||||
url: "https://www.brazabank.com.br/en/usdben/",
|
||||
description: "USDB, by Braza Group, is a USD-pegged stablecoin backed by U.S. and Brazilian bonds.",
|
||||
},
|
||||
{
|
||||
id: "europ",
|
||||
label: "EURØP",
|
||||
url: "https://schuman.io/europ/",
|
||||
description: "EURØP, issued by Schuman Financial, is the first MiCA-compliant euro stablecoin on XRPL.",
|
||||
},
|
||||
{
|
||||
id: "xsgd",
|
||||
label: "XSGD",
|
||||
url: "https://www.straitsx.com/xsgd",
|
||||
description: "XSGD, from StraitsX, is a Singapore Dollar-backed stablecoin regulated by MAS (Monetary Authority of Singapore).",
|
||||
},
|
||||
{
|
||||
id: "audd",
|
||||
label: "AUDD",
|
||||
url: "https://www.audd.digital/",
|
||||
description: "AUDD, an Australian dollar stablecoin, is live on XRPL and backed 1:1 with AUD.",
|
||||
},
|
||||
];
|
||||
|
||||
const embeddedPaymentsCards = [
|
||||
{
|
||||
id: 'digital-wallets',
|
||||
title: 'Digital Wallets',
|
||||
description: 'Offer fast, low-fee stablecoin payments between users and applications.',
|
||||
},
|
||||
{
|
||||
id: 'cross-border-remittance',
|
||||
title: 'Cross-Border Remittance',
|
||||
description: 'Use secure payment channels and the most optimal liquidity pathways for global remittances with RLUSD.',
|
||||
},
|
||||
{
|
||||
id: 'regulated-foreign-exchange',
|
||||
title: 'Regulated Foreign Exchange',
|
||||
description: 'Tap into a set of fiat-backed stablecoins, instantaneous swaps for efficient Foreign Exchange.',
|
||||
},
|
||||
{
|
||||
id: 'merchant-settlement',
|
||||
title: 'Merchant Settlement',
|
||||
description: 'Settle daily payments across assets using escrow or checks with compliance-focused features.',
|
||||
},
|
||||
{
|
||||
id: 'b2b-payment-rails',
|
||||
title: 'B2B Payment Rails',
|
||||
description: 'Build programmable payment flows with conditions and real-time data feeds.',
|
||||
},
|
||||
{
|
||||
id: 'compliance-first-payment-acceptance',
|
||||
title: 'Compliance-First Payment Acceptance',
|
||||
description: 'Add Deposit Authorization and whitelisting to comply with AML and KYC workflows.',
|
||||
},
|
||||
];
|
||||
|
||||
const battleTestedProjects = [
|
||||
{
|
||||
id: "coinpayments",
|
||||
label: "CoinPayments",
|
||||
url: "https://xrpl.org/blog/2025/coinpayments-xrpl-case-study-payment-processing",
|
||||
description: "CoinPayments uses XRPL's fast and low-cost payment rails to enable merchants to accept digital assets globally with near-instant settlement and minimal transaction fees.",
|
||||
buttonText: "Case Study"
|
||||
},
|
||||
{
|
||||
id: "ripple",
|
||||
label: "Ripple",
|
||||
url: "https://ripple.com/solutions/cross-border-payments/",
|
||||
description: "Ripple Payments enables crypto companies, payment service providers and fintech to facilitate real-time cross-border payments using stablecoins, digital assets and local currencies — with XRPL as a foundational transaction layer.",
|
||||
buttonText: "Case Study"
|
||||
},
|
||||
{
|
||||
id: "friipay",
|
||||
label: "FriiPay",
|
||||
url: "https://xrpl.org/blog/2025/frii-pay-xrpl-case-study-crypto-payment-solution",
|
||||
description: "FriiPay connects XRPL-based crypto wallets to point-of-sale terminals, allowing customers to pay with RLUSD or XRP while helping merchants save costs on card processing fees.",
|
||||
buttonText: "Case Study"
|
||||
},
|
||||
];
|
||||
|
||||
const integrationFeatures = [
|
||||
{
|
||||
title: "Access open documentation",
|
||||
link: "/docs/"
|
||||
},
|
||||
{
|
||||
title: "Use the Payments APIs + XRPL tooling",
|
||||
link: "/resources/dev-tools"
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
const paymentsResourcesCards = [
|
||||
{
|
||||
title: "Developer Spotlight",
|
||||
description: "Are you building a peer-to-peer payments solution, integrating stablecoins, or exploring RLUSD on the XRP Ledger?",
|
||||
links: [
|
||||
{
|
||||
text: "Share Your Work",
|
||||
url: "https://discord.gg/sfX3ERAMjH"
|
||||
}
|
||||
],
|
||||
backgroundClass: "developer-spotlight"
|
||||
},
|
||||
{
|
||||
title: "Learn & Stay Updated",
|
||||
description: "Stay ahead of the curve with the latest developments in XRPL Payments by joining the Developer Discord and signing up for the XRPL Community Newsletter.",
|
||||
links: [
|
||||
{
|
||||
text: "Join the Developer Discord",
|
||||
url: "https://discord.gg/sfX3ERAMjH"
|
||||
},
|
||||
{
|
||||
text: "Sign up for the Newsletter",
|
||||
url: "https://xrplresources.org/subscribe"
|
||||
}
|
||||
],
|
||||
backgroundClass: "learn-stay-updated"
|
||||
}
|
||||
];
|
||||
|
||||
export default function PaymentsPage() {
|
||||
return (
|
||||
<main className="use-case-payments">
|
||||
<section className="use-case-payments__hero">
|
||||
<div className="video-content">
|
||||
<iframe
|
||||
width="100%"
|
||||
height="100%"
|
||||
src={video.src}
|
||||
title={video.title}
|
||||
frameBorder={0}
|
||||
allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
/>
|
||||
|
||||
</div>
|
||||
<div className="text-content">
|
||||
<h6 className="eyebrow mb-3 text-large">
|
||||
{translate("Payments")}
|
||||
</h6>
|
||||
<h2 className="h4 h2-sm mb-10">
|
||||
{translate("Payments Suite")}
|
||||
</h2>
|
||||
<p className="mb-10">
|
||||
{translate(
|
||||
"Helping fintechs and payment providers move money fast, globally, and at low cost - all through simple APIs."
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<AdvantagesSection
|
||||
title="Why Choose XRPL Payments Suite for Your Payment Rails?"
|
||||
advantages={paymentAdvantages}
|
||||
useLinks={false}
|
||||
className="payments-advantages-spacing"
|
||||
/>
|
||||
<ProjectCards
|
||||
title="Enterprise-Grade Stablecoins, Issued Natively on XRPL"
|
||||
projects={paymentProjects}
|
||||
showCarousel={false}
|
||||
className="mt-12 px-0"
|
||||
/>
|
||||
<BenefitsSection
|
||||
title="Unlock New Business Models with Embedded Payments"
|
||||
description="XRPL Payments supports modern fintech use cases with plug-and-play APIs or partner-led deployments."
|
||||
cards={embeddedPaymentsCards}
|
||||
showImages={true}
|
||||
className="embedded-payments-section px-0"
|
||||
listId="embedded-payments-list"
|
||||
/>
|
||||
|
||||
<ProjectCards
|
||||
title="Payments Solution, Battle-Tested by Industry Leaders"
|
||||
projects={battleTestedProjects}
|
||||
showCarousel={false}
|
||||
className="battle-tested-section px-0"
|
||||
/>
|
||||
|
||||
<section className="payments-integration-section">
|
||||
<div className="developer-tools">
|
||||
<div className="">
|
||||
<header className="developer-tools__header text-center">
|
||||
<h2 className="developer-tools__title">
|
||||
{translate("Flexible Integration: DIY or Partner-Led")}
|
||||
</h2>
|
||||
</header>
|
||||
<div className="row">
|
||||
<div className="col-lg-6">
|
||||
<div className="integration-column">
|
||||
<h3 className="integration-column__title">
|
||||
{translate("Build It Yourself")}
|
||||
</h3>
|
||||
<p className="integration-column__subtitle">
|
||||
{translate("Ideal for seasoned teams with crypto experience")}
|
||||
</p>
|
||||
<ul className="developer-tools__list">
|
||||
{integrationFeatures.map((feature, index) => (
|
||||
<FeatureItem
|
||||
key={index}
|
||||
link={feature.link}
|
||||
title={feature.title}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-lg-6">
|
||||
<div className="integration-column">
|
||||
<h3 className="integration-column__title">
|
||||
{translate("Work with a Partner")}
|
||||
</h3>
|
||||
<p className="integration-column__subtitle">
|
||||
{translate("Ideal for regulated institutions")}
|
||||
</p>
|
||||
<ul className="developer-tools__list">
|
||||
<FeatureItem
|
||||
link="https://discord.com/invite/KTNmhJDXqa"
|
||||
title="Connect with the Community"
|
||||
/>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<DeveloperResourcesSection cards={paymentsResourcesCards} />
|
||||
</main>
|
||||
<div className="landing">
|
||||
<HeroSection />
|
||||
<WhyChooseSection />
|
||||
<AdvancedFeaturesSection />
|
||||
<StablecoinsSection />
|
||||
<EmbeddedPaymentsSection />
|
||||
<PartnerLogosSection />
|
||||
<FlexibleIntegrationSection />
|
||||
<DeveloperSpotlightSection />
|
||||
<StayConnectedSection />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PaymentsPage;
|
||||
}
|
||||
|
||||
@@ -1,305 +1,318 @@
|
||||
import React, { useRef, useState } from "react";
|
||||
import * as React from 'react';
|
||||
import { useThemeHooks } from '@redocly/theme/core/hooks';
|
||||
import { NavList } from "shared/components/nav-list";
|
||||
import { Link } from "@redocly/theme/components/Link/Link";
|
||||
import { AdvantagesSection } from "shared/components/advantages-section";
|
||||
import { ProjectCards } from "shared/components/project-cards";
|
||||
import { HeaderHeroSplitMedia } from 'shared/sections/HeaderHeroSplitMedia/HeaderHeroSplitMedia';
|
||||
import { CarouselCardList } from 'shared/sections/CarouselCardList/CarouselCardList';
|
||||
import { LogoRectangleGrid } from 'shared/sections/LogoRectangleGrid/LogoRectangleGrid';
|
||||
import { CarouselFeatured } from 'shared/patterns/CarouselFeatured/CarouselFeatured';
|
||||
import { SmallTilesSection } from 'shared/sections/SmallTilesSection/SmallTilesSection';
|
||||
import { LinkTextDirectory } from 'shared/sections/LinkTextDirectory/LinkTextDirectory';
|
||||
import { FeatureTwoColumn } from 'shared/sections/FeatureTwoColumn/FeatureTwoColumn';
|
||||
|
||||
export const frontmatter = {
|
||||
seo: {
|
||||
title: "Tokenization",
|
||||
title: 'Real-World Asset (RWA) Tokenization on XRPL',
|
||||
description:
|
||||
"Explore the possibilities of tokenization on the XRP Ledger, revolutionizing ownership, transactions, and value exchange with unparalleled efficiency and security.",
|
||||
'Learn how to issue crypto tokens and build tokenization solutions on the XRP Ledger with developer tools and APIs purpose-built for finance.',
|
||||
},
|
||||
};
|
||||
|
||||
const useCases = [
|
||||
{
|
||||
description: "Stablecoin Issuer",
|
||||
link: "/docs/use-cases/tokenization/stablecoin-issuer/",
|
||||
},
|
||||
{
|
||||
description: "NFT Marketplace",
|
||||
link: "/docs/use-cases/tokenization/nftoken-marketplace/",
|
||||
},
|
||||
{
|
||||
description: "Multi-purpose Token Issuer",
|
||||
link: "/docs/use-cases/tokenization/creating-an-asset-backed-multi-purpose-token"
|
||||
},
|
||||
{
|
||||
description: "Authorized Minter",
|
||||
link: "/docs/use-cases/tokenization/authorized-minter/",
|
||||
},
|
||||
{
|
||||
description: "Digital Artist",
|
||||
link: "/docs/use-cases/tokenization/digital-artist/",
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
|
||||
const securityAdvantages = [
|
||||
{
|
||||
id: "trustlines",
|
||||
title: "Trust Lines & Authorized Trust Lines",
|
||||
contents: [
|
||||
{
|
||||
href: "/docs/concepts/tokens/fungible-tokens/",
|
||||
subtitle: "Trust Lines",
|
||||
description: "No spamming of wallets without permission.",
|
||||
},
|
||||
{
|
||||
href: "/docs/concepts/tokens/fungible-tokens/authorized-trust-lines/",
|
||||
subtitle: "Authorized Trustlines",
|
||||
description: "Control who can hold your tokens with allowlisting.",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "freeze-clawbacks",
|
||||
title: "Freeze & Clawbacks",
|
||||
contents: [
|
||||
{
|
||||
href: "/docs/concepts/tokens/fungible-tokens/",
|
||||
subtitle: "Freeze",
|
||||
description: "If you see signs of suspicious activity, you can suspend trading of your token while investigating the issue.",
|
||||
},
|
||||
{
|
||||
href: "/docs/concepts/tokens/fungible-tokens/clawing-back-tokens/",
|
||||
subtitle: "Clawback",
|
||||
description: "Recover tokens distributed to accounts in error: for example, reclaim funds sent to an account sanctioned for illegal activity.",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "ntf-tokens",
|
||||
title: "Non-transferable Tokens",
|
||||
contents: [
|
||||
{
|
||||
href: "/docs/concepts/tokens/nfts/non-transferable-tokens/",
|
||||
subtitle: "Transferable flag",
|
||||
description: "Native support for nontransferable items such as identity tokens, airline credits, and consumer rewards, honored by all on-chain participants.",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "royalties",
|
||||
title: "Royalties",
|
||||
contents: [
|
||||
{
|
||||
href: "/docs/references/protocol/data-types/nftoken/#transferfee",
|
||||
subtitle: "NFT Transfer Fees",
|
||||
description: "Reliably collect your percentage of the sale price of your tokens.",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const stats = [
|
||||
{
|
||||
id: "nfts-minted",
|
||||
stat: "20K",
|
||||
description: "NFTs minted",
|
||||
},
|
||||
{
|
||||
id: "nft-trading-vol",
|
||||
stat: "~1000",
|
||||
description: "NFT trading volume",
|
||||
},
|
||||
{
|
||||
id: "network-speed",
|
||||
stat: "4",
|
||||
description: "Seconds to confirmation",
|
||||
},
|
||||
{
|
||||
id: "fee-per-tx",
|
||||
stat: "$0.001",
|
||||
description: "Avg fee per NFT tx",
|
||||
},
|
||||
];
|
||||
|
||||
const projects = [
|
||||
{
|
||||
id: "xrpcafe",
|
||||
label: "XRP cafe",
|
||||
url: "https://xrp.cafe",
|
||||
},
|
||||
{
|
||||
id: "onXRP",
|
||||
label: "onXRP",
|
||||
url: "https://nft.onxrp.com",
|
||||
},
|
||||
{
|
||||
id: "xaman",
|
||||
label: "Xaman labs",
|
||||
url: "https://Xaman.app",
|
||||
},
|
||||
{
|
||||
id: "amy",
|
||||
label: "amy.network",
|
||||
url: "https://token.amy.network",
|
||||
},
|
||||
{
|
||||
id: "sologenic",
|
||||
label: "Sologenic",
|
||||
url: "https://sologenic.org/trade",
|
||||
},
|
||||
{
|
||||
id: "carbonland",
|
||||
label: "Carbonland trust",
|
||||
url: "https://www.carbonlandtrust.com",
|
||||
},
|
||||
{
|
||||
id: "nautilus",
|
||||
label: "Nautilus wallet",
|
||||
url: "https://github.com/nautls/nautilus-wallet",
|
||||
},
|
||||
{
|
||||
id: "evernode",
|
||||
label: "Evernode",
|
||||
url: "https://evernode.org",
|
||||
},
|
||||
{
|
||||
id: "raised-in-space",
|
||||
label: "Raised in space",
|
||||
url: "https://raisedinspace.com",
|
||||
},
|
||||
];
|
||||
|
||||
const articles = [
|
||||
{
|
||||
time: "NOV 2023",
|
||||
title: "NFTs and the XRPL: A Marriage of Art and Technology",
|
||||
url: "https://medium.com/@MariaSolorzano/title-nfts-and-the-xrpl-a-marriage-of-art-and-technology-cf76a0432693",
|
||||
},
|
||||
// TODO: Add more articles that aren't focused on price speculation
|
||||
];
|
||||
|
||||
|
||||
|
||||
export default function Tokenization() {
|
||||
const modalRef = useRef(null);
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
return (
|
||||
<div className="landing page-tokenization">
|
||||
<div className="position-relative"></div>
|
||||
<div className="position-relative d-none-sm"></div>
|
||||
<section className="container-new mt-lg-16">
|
||||
<div className="card-grid card-grid-2xN">
|
||||
<div className="col">
|
||||
<img className="tokenization-graphic mw-100 hide-md" />
|
||||
</div>
|
||||
<div className="col">
|
||||
<div className="d-flex flex-column-reverse">
|
||||
<img className="tokenization-graphic mw-100 show-md" />
|
||||
<h2 className="h4 h2-sm mb-10">
|
||||
{translate(
|
||||
"Work with a variety of tokens supported by the XRP Ledger."
|
||||
)}
|
||||
</h2>
|
||||
<h6 className="eyebrow mb-3 text-large">
|
||||
{translate("Tokenization")}
|
||||
</h6>
|
||||
</div>
|
||||
<p className="mb-10">
|
||||
{translate(
|
||||
"Explore the possibilities of tokenization on the XRP Ledger, revolutionizing ownership, transactions, and value exchange with unparalleled efficiency and security."
|
||||
)}
|
||||
</p>
|
||||
<div className="tokenization-color-bar" />
|
||||
<div className="mb-10">
|
||||
<NavList pages={useCases} bottomBorder={false} />
|
||||
</div>
|
||||
<div className="d-flex">
|
||||
<Link
|
||||
className="btn btn-primary d-inline-flex align-items-center"
|
||||
to="/docs"
|
||||
>
|
||||
{translate("Quick Start")}
|
||||
</Link>{" "}
|
||||
<a
|
||||
className="ml-4 video-external-link btn-none"
|
||||
target="_blank"
|
||||
href="https://www.youtube.com/playlist?list=PLJQ55Tj1hIVZtJ_JdTvSum2qMTsedWkNi"
|
||||
>
|
||||
<i className="fa fa-video-camera" />
|
||||
<span className="link-text">{translate("Learn more")}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<AdvantagesSection
|
||||
title="Security advantages"
|
||||
description="Check out the security features you can tap into right from the chain without the hassle of piecing together smart contracts."
|
||||
advantages={securityAdvantages}
|
||||
useLinks
|
||||
<div className="landing page-use-cases page-use-cases-tokenization">
|
||||
|
||||
{/* 1. Hero */}
|
||||
<HeaderHeroSplitMedia
|
||||
layout="content-left"
|
||||
title={translate('Real-World Asset (RWA) Tokenization')}
|
||||
subtitle=""
|
||||
description={translate(
|
||||
'Learn how to issue crypto tokens and build tokenization solutions with developer tools and APIs.'
|
||||
)}
|
||||
primaryCta={{
|
||||
label: translate('Get Started Now'),
|
||||
href: '/docs/use-cases/tokenization/creating-an-asset-backed-multi-purpose-token',
|
||||
}}
|
||||
media={{
|
||||
src: require('../../../static/img/bds-2026/use-cases-tokenization-hero-media.jpg'),
|
||||
alt: translate('Real-World Asset Tokenization on XRPL'),
|
||||
}}
|
||||
/>
|
||||
|
||||
<section className="container-new py-20">
|
||||
<div className="d-flex flex-column-reverse">
|
||||
<h4 className="eyebrow mb-16">{translate("Stats")}</h4>
|
||||
</div>
|
||||
<div className="tokenization-stats">
|
||||
{stats.map((statElement) => (
|
||||
<div
|
||||
className="stat-container align-items-center"
|
||||
key={statElement.id}
|
||||
>
|
||||
<div className="stat">{statElement.stat}</div>
|
||||
<div className="desc">{translate(statElement.description)}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
<ProjectCards
|
||||
title="Featured real world projects"
|
||||
projects={projects}
|
||||
showCarousel={true}
|
||||
{/* 2. Why Financial Developers Choose XRPL — Carousel (4 cards from Assets frame) */}
|
||||
<CarouselCardList
|
||||
variant="green"
|
||||
buttonVariant="green"
|
||||
heading={translate('Why Financial Developers Choose XRPL as an RWA Tokenization Platform')}
|
||||
description=""
|
||||
cards={[
|
||||
{
|
||||
icon: require('../../../static/img/icons/2026/black/fast-settlement-and-low-fees.svg'),
|
||||
title: translate('Fast Settlement and Low Fees'),
|
||||
description: translate(
|
||||
'Settle transactions in 3-5 seconds for a fraction of a cent — ideal for large-scale, high-volume RWA tokenization.'
|
||||
),
|
||||
},
|
||||
{
|
||||
icon: require('../../../static/img/icons/2026/black/onchain-metadata.svg'),
|
||||
title: translate('Onchain Metadata'),
|
||||
description: translate(
|
||||
'Easily store key asset information or link to off-chain data using simple APIs, giving token holders more transparency and functionality.'
|
||||
),
|
||||
},
|
||||
{
|
||||
icon: require('../../../static/img/icons/2026/black/native-compliance-capabilities.svg'),
|
||||
title: translate('Native Compliance Capabilities'),
|
||||
description: translate(
|
||||
'Automatically check investor credentials, control who can transfer assets, and keep a full record of every transaction, offering a secure framework for the expanding RWA crypto market.'
|
||||
),
|
||||
},
|
||||
{
|
||||
icon: require('../../../static/img/icons/2026/black/delegated-token-management.svg'),
|
||||
title: translate('Delegated Token Management'),
|
||||
description: translate(
|
||||
'Use a robust permission system to let trusted third parties manage the token on your behalf.'
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 3. Multi Purpose Tokens (MPTs) */}
|
||||
<FeatureTwoColumn
|
||||
color="neutral"
|
||||
arrange="right"
|
||||
title={translate('Multi Purpose Tokens (MPTs)')}
|
||||
description={translate(
|
||||
"Issue, manage, and trade real-world assets without needing to build smart contracts. XRP Ledger's built-in functionality and compliance-enabling features allow asset tokenization without additional layers of complexity."
|
||||
)}
|
||||
links={[
|
||||
{
|
||||
label: translate('Download Whitepaper'),
|
||||
href: 'https://xrpl.org/static/pdf/Whitepaper_the_future_of_asset_tokenization.pdf',
|
||||
},
|
||||
]}
|
||||
media={{
|
||||
src: require('../../../static/img/bds-2026/use-cases-tokenization-mpts-media.jpg'),
|
||||
alt: translate('Multi Purpose Tokens on XRPL'),
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 4. Trusted by Leaders — Logo Grid (8 distinct partner logos from Assets frame) */}
|
||||
<LogoRectangleGrid
|
||||
variant="gray"
|
||||
heading={translate('Trusted by Leaders in Real-World Asset Tokenization')}
|
||||
logos={[
|
||||
{ logo: require('../../../static/img/logos/black/circle.png'), alt: translate('Circle') },
|
||||
{ logo: require('../../../static/img/logos/black/ondo.png'), alt: translate('Ondo Finance') },
|
||||
{ logo: require('../../../static/img/logos/black/db-schenker.png'), alt: translate('DB Schenker') },
|
||||
{ logo: require('../../../static/img/logos/black/ripple.png'), alt: translate('Ripple') },
|
||||
{ logo: require('../../../static/img/logos/black/societe-generale.png'), alt: translate('Société Générale') },
|
||||
{ logo: require('../../../static/img/logos/black/zeconomy.png'), alt: translate('Zeconomy') },
|
||||
{ logo: require('../../../static/img/logos/black/vert.png'), alt: translate('Vert') },
|
||||
{ logo: require('../../../static/img/logos/black/braza.png'), alt: translate('Braza') },
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 5. Token Utility and Market Integration — Carousel Featured (4 slides from Assets frame) */}
|
||||
<CarouselFeatured
|
||||
background="neutral"
|
||||
slides={[
|
||||
{
|
||||
id: 'token-utility-trading',
|
||||
heading: translate('Token Utility and Market Integration'),
|
||||
features: [
|
||||
{
|
||||
title: translate('Trading'),
|
||||
description: translate(
|
||||
"Utilize XRP Ledger's native decentralized exchange (DEX) with integrated Automated Market Makers (AMM) and onchain 24/7 order books, providing a developer-friendly environment to create DeFi solutions for traditional finance applications."
|
||||
),
|
||||
},
|
||||
],
|
||||
buttons: [
|
||||
{
|
||||
label: translate('Decentralized exchange (DEX)'),
|
||||
href: '/docs/concepts/tokens/decentralized-exchange',
|
||||
},
|
||||
{
|
||||
label: translate('Escrow functionality'),
|
||||
href: '/docs/concepts/payment-types/escrow',
|
||||
},
|
||||
],
|
||||
imageSrc: require('../../../static/img/bds-2026/use-cases-tokenization-token-utility-1-trading.jpg'),
|
||||
imageAlt: translate('Trading on the XRPL DEX'),
|
||||
},
|
||||
{
|
||||
id: 'token-utility-collateral',
|
||||
heading: translate('Token Utility and Market Integration'),
|
||||
features: [
|
||||
{
|
||||
title: translate('Collateral Mobility'),
|
||||
description: translate(
|
||||
'Issuers can enable escrow functionality to lock tokens and facilitate secure, conditional transfers of assets based on time-locks or other conditions to enable automated financial use cases onchain.'
|
||||
),
|
||||
},
|
||||
],
|
||||
buttons: [
|
||||
{
|
||||
label: translate('Decentralized exchange (DEX)'),
|
||||
href: '/docs/concepts/tokens/decentralized-exchange',
|
||||
},
|
||||
{
|
||||
label: translate('Escrow functionality'),
|
||||
href: '/docs/concepts/payment-types/escrow',
|
||||
},
|
||||
],
|
||||
imageSrc: require('../../../static/img/bds-2026/use-cases-tokenization-token-utility-2-collateral.jpg'),
|
||||
imageAlt: translate('Collateral Mobility via Escrow'),
|
||||
},
|
||||
{
|
||||
id: 'token-utility-delegated',
|
||||
heading: translate('Token Utility and Market Integration'),
|
||||
features: [
|
||||
{
|
||||
title: translate('Delegated Distribution'),
|
||||
description: translate(
|
||||
'Token issuers can delegate user onboarding and token movement to distribution specialists, enabling them to scale their distribution network easily with a single transaction, while avoiding complex off-chain development.'
|
||||
),
|
||||
},
|
||||
],
|
||||
buttons: [
|
||||
{
|
||||
label: translate('Decentralized exchange (DEX)'),
|
||||
href: '/docs/concepts/tokens/decentralized-exchange',
|
||||
},
|
||||
{
|
||||
label: translate('Escrow functionality'),
|
||||
href: '/docs/concepts/payment-types/escrow',
|
||||
},
|
||||
],
|
||||
imageSrc: require('../../../static/img/bds-2026/use-cases-tokenization-token-utility-3-delegated.jpg'),
|
||||
imageAlt: translate('Delegated Distribution'),
|
||||
},
|
||||
{
|
||||
id: 'token-utility-crosschain',
|
||||
heading: translate('Token Utility and Market Integration'),
|
||||
features: [
|
||||
{
|
||||
title: translate('Cross-Chain Markets'),
|
||||
description: translate(
|
||||
'Access cross-chain trading and lending markets to enhance token utility and liquidity across ecosystems, a critical component for sustaining long-term XRP ledger tokenization growth.'
|
||||
),
|
||||
},
|
||||
],
|
||||
buttons: [
|
||||
{
|
||||
label: translate('Decentralized exchange (DEX)'),
|
||||
href: '/docs/concepts/tokens/decentralized-exchange',
|
||||
},
|
||||
{
|
||||
label: translate('Escrow functionality'),
|
||||
href: '/docs/concepts/payment-types/escrow',
|
||||
},
|
||||
],
|
||||
imageSrc: require('../../../static/img/bds-2026/use-cases-tokenization-token-utility-4-crosschain.jpg'),
|
||||
imageAlt: translate('Cross-Chain Markets'),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 6. Developer Tools & APIs — Small Tiles */}
|
||||
<SmallTilesSection
|
||||
headline={translate('Developer Tools & APIs')}
|
||||
subtitle={translate(
|
||||
"Streamline development and build powerful RWA tokenization solutions with XRP Ledger's comprehensive developer toolset:"
|
||||
)}
|
||||
cardVariant="green"
|
||||
cards={[
|
||||
{
|
||||
icon: require('../../../static/img/logos/black/js.svg'),
|
||||
iconAlt: translate('JavaScript'),
|
||||
label: translate('Get Started with Javascript'),
|
||||
href: 'https://github.com/XRPLF/xrpl.js',
|
||||
},
|
||||
{
|
||||
icon: require('../../../static/img/logos/black/python.svg'),
|
||||
iconAlt: translate('Python'),
|
||||
label: translate('Get Started with Python'),
|
||||
href: 'https://github.com/XRPLF/xrpl-py',
|
||||
},
|
||||
{
|
||||
icon: require('../../../static/img/icons/2026/black/dex-integration.svg'),
|
||||
iconAlt: translate('DEX Integration'),
|
||||
label: translate('DEX Integration'),
|
||||
href: '/docs/tutorials/how-tos/use-tokens/trade-in-the-decentralized-exchange',
|
||||
},
|
||||
{
|
||||
icon: require('../../../static/img/logos/black/xrpl-evm.svg'),
|
||||
iconAlt: translate('XRPL EVM Sidechain'),
|
||||
label: translate('Cross-Chain Interoperability'),
|
||||
href: 'https://docs.xrplevm.org/docs/axelar/intro-to-axelar/',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 7. Defining Features on XRPL — Link Text Directory */}
|
||||
<LinkTextDirectory
|
||||
heading={translate('Defining Features on XRPL')}
|
||||
cards={[
|
||||
{
|
||||
heading: translate('Proven Open-Source Technology'),
|
||||
description: translate(
|
||||
'With over 3.3B transactions processed, XRP Ledger has been a trusted, battle-tested blockchain for over a decade, supported by a global developer community committed to financial innovation.'
|
||||
),
|
||||
buttons: [],
|
||||
},
|
||||
{
|
||||
heading: translate('Purpose-Built for Finance'),
|
||||
description: translate(
|
||||
'XRP Ledger provides out-of-the-box institutional-grade functionality, reducing development overhead and eliminating the need for smart contracts, which makes it ideal for the RWA crypto space.'
|
||||
),
|
||||
buttons: [],
|
||||
},
|
||||
{
|
||||
heading: translate('Native Compliance & Security'),
|
||||
description: translate(
|
||||
'Maintain control over tokenized assets and enforce compliance using native tools such as issuer-defined Authorization, onchain Freeze capabilities, detailed metadata for attestations, and multi-signature accounts.'
|
||||
),
|
||||
buttons: [],
|
||||
},
|
||||
{
|
||||
heading: translate('Optimal Liquidity Pathways'),
|
||||
description: translate(
|
||||
"Streamline cross-currency transactions and trading as XRP Ledger's embedded trading features automatically identify the most efficient routes to enhance liquidity issued tokens and XRP."
|
||||
),
|
||||
buttons: [
|
||||
{
|
||||
label: translate('Explore Trading on the XRPL'),
|
||||
href: '/docs/tutorials/how-tos/use-tokens/trade-in-the-decentralized-exchange',
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 8. Stay Connected — Feature Two Column (Assets-frame variant: "Sign Up") */}
|
||||
<FeatureTwoColumn
|
||||
color="yellow"
|
||||
arrange="right"
|
||||
title={translate('Stay Connected')}
|
||||
description={translate(
|
||||
'Stay ahead in the world of tokenization. Subscribe to receive the latest insights, trends, and updates on payment solutions — delivered directly to your inbox.'
|
||||
)}
|
||||
links={[
|
||||
{
|
||||
label: translate('Sign Up'),
|
||||
href: 'https://share.hsforms.com/18zNvJDR4QbObGPLDh3n5Bw4vgrs',
|
||||
},
|
||||
]}
|
||||
media={{
|
||||
src: require('../../../static/img/bds-2026/use-cases-tokenization-stay-connected-media.jpg'),
|
||||
alt: translate('Stay Connected with XRPL'),
|
||||
}}
|
||||
/>
|
||||
|
||||
<section className="container-new py-20">
|
||||
<div className="d-flex flex-column-reverse">
|
||||
<h4 className="eyebrow mb-16">{translate("Related Articles")}</h4>
|
||||
</div>
|
||||
<div className="related-articles card-grid card-grid-3xN mb-16">
|
||||
{articles.map((article, index) => (
|
||||
<div className="article-card-container" key={`article-${index}`}>
|
||||
<div className="article-card-background" />
|
||||
<a
|
||||
className="article-card col p-8 float-up-on-hover"
|
||||
target="_blank"
|
||||
href={article.url}
|
||||
>
|
||||
<div className="time h4 normal mb-8">
|
||||
{translate(article.time)}
|
||||
</div>
|
||||
<div className="h5 mb-4">{translate(article.title)}</div>
|
||||
<div className="btn btn-primary btn-arrow-out">
|
||||
{translate("Read More")}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="more-related">
|
||||
<div className="d-flex flex-column-reverse">
|
||||
<h4 className="eyebrow mb-16">
|
||||
{translate("More related articles")}
|
||||
</h4>
|
||||
</div>
|
||||
<div className="video-external-link">
|
||||
<a href="https://coincodecap.com/ripple-xrp">
|
||||
{translate(
|
||||
"Ripple (XRP): A Dive into its Working, Tokenomics, Price Factor and SEC Lawsuit"
|
||||
)}
|
||||
</a>
|
||||
</div>
|
||||
<div className="subtitle">{translate("November 2023")}</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -399,13 +399,6 @@ function TokenHeroSection() {
|
||||
const { translate } = useTranslate();
|
||||
return (
|
||||
<section className="token-hero-section">
|
||||
<div className="position-relative d-none-sm">
|
||||
<img
|
||||
alt="orange waves"
|
||||
src={require("./../../../static/img/backgrounds/events-orange.svg")}
|
||||
id="events-orange"
|
||||
/>
|
||||
</div>
|
||||
<div className="token-title-container">
|
||||
<h1 className="token-title">
|
||||
{translate("Real-World Asset (RWA) Tokenization")}
|
||||
|
||||
264
docs/use-cases/trading/index.page.tsx
Normal file
264
docs/use-cases/trading/index.page.tsx
Normal file
@@ -0,0 +1,264 @@
|
||||
import { useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
import { HeaderHeroSplitMedia } from "shared/sections/HeaderHeroSplitMedia/HeaderHeroSplitMedia";
|
||||
import FeaturedVideoHero from "shared/sections/FeaturedVideoHero/FeaturedVideoHero";
|
||||
import { LinkTextDirectory } from "shared/sections/LinkTextDirectory/LinkTextDirectory";
|
||||
import { CardsFeatured } from "shared/sections/CardsFeatured/CardsFeatured";
|
||||
import { CarouselCardList } from "shared/sections/CarouselCardList/CarouselCardList";
|
||||
import { LogoRectangleGrid } from "shared/sections/LogoRectangleGrid/LogoRectangleGrid";
|
||||
import { CardsTextGrid } from "shared/sections/CardsTextGrid/CardsTextGrid";
|
||||
|
||||
export const frontmatter = {
|
||||
seo: {
|
||||
title: "Trading on the XRP Ledger",
|
||||
description:
|
||||
"Institutional DeFi (iDeFi) on the XRPL delivers high-speed, low-cost, compliance-first infrastructure for crypto trading and liquidity.",
|
||||
},
|
||||
};
|
||||
|
||||
export default function Trading() {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
return (
|
||||
<div className="landing">
|
||||
<HeaderHeroSplitMedia
|
||||
title={translate("Trading on the XRP Ledger")}
|
||||
subtitle=""
|
||||
description={translate(
|
||||
"Institutional DeFi (iDeFi) on the XRPL delivers high-speed, low-cost, compliance-first infrastructure for crypto trading and liquidity. Built for financial institutions, Web2 platforms, and crypto-native businesses, XRPL’s DEX and protocol ecosystem reduces complexity while preserving trust, transparency, and scale."
|
||||
)}
|
||||
media={{
|
||||
src: require("../../../static/img/bds-2026/trading-hero-media.jpg"),
|
||||
alt: translate("Trading on the XRP Ledger"),
|
||||
}}
|
||||
/>
|
||||
|
||||
<FeaturedVideoHero
|
||||
headline={translate("Compliance-First Trading on XRPL")}
|
||||
subtitle={translate(
|
||||
"Credentials and Permissioned Domains are native features that unlock private DeFi, enable regulated asset trading, and enforce KYC AML directly on chain. These tools create secure, programmable environments for trading tokenized money market funds, treasuries, and more."
|
||||
)}
|
||||
video={{
|
||||
source: {
|
||||
type: "embed",
|
||||
embedUrl: "https://fast.wistia.net/embed/iframe/14coho0rw2",
|
||||
},
|
||||
coverImage: {
|
||||
src: require("../../../static/img/bds-2026/trading-video-poster.jpg"),
|
||||
alt: translate("Compliance-First Trading on XRPL"),
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
<LinkTextDirectory
|
||||
heading={translate("Why Trade on XRPL?")}
|
||||
cards={[
|
||||
{
|
||||
heading: translate("Instant Settlement and Low Fees"),
|
||||
description: translate(
|
||||
"Settle transactions in 3-5 seconds for a fraction of a cent."
|
||||
),
|
||||
buttons: [],
|
||||
},
|
||||
{
|
||||
heading: translate("No Smart Contract Risk"),
|
||||
description: translate(
|
||||
"Built into XRPL for safer use: no smart contracts, no MEV, and full control of your funds."
|
||||
),
|
||||
buttons: [],
|
||||
},
|
||||
{
|
||||
heading: translate("Unified Liquidity"),
|
||||
description: translate(
|
||||
"Unified liquidity pools for better pricing and execution."
|
||||
),
|
||||
buttons: [],
|
||||
},
|
||||
{
|
||||
heading: translate("Compliance-First"),
|
||||
description: translate(
|
||||
"Asset controls and permissioned environments for regulatory alignment."
|
||||
),
|
||||
buttons: [],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<CardsFeatured
|
||||
heading={translate("Use Cases to Meet Your Needs")}
|
||||
description=""
|
||||
cards={[
|
||||
{
|
||||
image: require("../../../static/img/bds-2026/trading-feature-media-1.jpg"),
|
||||
imageAlt: translate("Financial Institutions"),
|
||||
title: translate("Financial Institutions"),
|
||||
subtitle: translate(
|
||||
"• Permissioned FX trading with on-chain liquidity\n• Tokenized asset exchanges with compliance controls\n• Integrate onchain credit markets to fund strategies"
|
||||
),
|
||||
buttonLabel: translate("Learn More"),
|
||||
href: "/docs/use-cases/defi",
|
||||
},
|
||||
{
|
||||
image: require("../../../static/img/bds-2026/trading-feature-media-2.jpg"),
|
||||
imageAlt: translate("Fintechs"),
|
||||
title: translate("Fintechs"),
|
||||
subtitle: translate(
|
||||
"• Embed instant, global payments in apps\n• Tap into a multi-asset liquidity ecosystem\n• Cross-border settlement without intermediaries"
|
||||
),
|
||||
buttonLabel: translate("Learn More"),
|
||||
href: "/docs/use-cases/payments",
|
||||
},
|
||||
{
|
||||
image: require("../../../static/img/bds-2026/trading-feature-media-3.jpg"),
|
||||
imageAlt: translate("Unified Liquidity"),
|
||||
title: translate("Unified Liquidity"),
|
||||
subtitle: translate(
|
||||
"• Deploy trading pairs without contract risk\n• Tap into a multi-asset liquidity ecosystem\n• Integrate lending and borrowing with native XRPL tools"
|
||||
),
|
||||
buttonLabel: translate("Learn More"),
|
||||
href: "/docs/concepts/tokens/decentralized-exchange",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<CarouselCardList
|
||||
variant="green"
|
||||
buttonVariant="green"
|
||||
heading={translate("Defining Trading Features on XRPL")}
|
||||
description=""
|
||||
cards={[
|
||||
{
|
||||
icon: require("../../../static/img/icons/2026/black/hybrid-dex.svg"),
|
||||
title: translate("Hybrid DEX"),
|
||||
description: translate(
|
||||
"Utilize XRP Ledger's native decentralized exchange (DEX) with integrated Automated Market Makers (AMM) and onchain 24/7 order books, providing a developer-friendly environment to create DeFi solutions for traditional finance applications."
|
||||
),
|
||||
href: "/docs/concepts/tokens/decentralized-exchange",
|
||||
},
|
||||
{
|
||||
icon: require("../../../static/img/icons/2026/black/compliance-primitive.svg"),
|
||||
title: translate("Compliance Primitive"),
|
||||
description: translate(
|
||||
"Use built-in logic to stay compliant: freeze funds, manage access, control order books, and perform on-chain KYC."
|
||||
),
|
||||
href: "/docs/concepts/transactions",
|
||||
},
|
||||
{
|
||||
icon: require("../../../static/img/icons/2026/black/token-standards.svg"),
|
||||
title: translate("Token Standards"),
|
||||
description: translate(
|
||||
"Adopt token standards that fit your business and regulatory needs: MPTs, stablecoins, IOUs, and NFTs."
|
||||
),
|
||||
href: "/docs/concepts/tokens",
|
||||
},
|
||||
{
|
||||
icon: require("../../../static/img/icons/2026/black/transaction-simulation.svg"),
|
||||
title: translate("Transaction Simulation"),
|
||||
description: translate(
|
||||
"Remove execution risk and achieve clarity on transaction economics with transaction simulation."
|
||||
),
|
||||
href: "/docs",
|
||||
},
|
||||
{
|
||||
icon: require("../../../static/img/icons/2026/black/native-lending-protocol.svg"),
|
||||
title: translate("Native Lending Protocol"),
|
||||
description: translate(
|
||||
"Access deep, affordable capital directly on-chain to power your trading strategies."
|
||||
),
|
||||
href: "/docs",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<LogoRectangleGrid
|
||||
variant="gray"
|
||||
heading={translate("Leading DeFi Protocols on XRPL")}
|
||||
description={translate(
|
||||
"A connected ecosystem of blockchain trading, lending, and liquidity solutions."
|
||||
)}
|
||||
logos={[
|
||||
{
|
||||
logo: require("../../../static/img/logos/black/hidden-road.png"),
|
||||
alt: translate("Hidden Road"),
|
||||
href: "https://www.hiddenroad.com/",
|
||||
},
|
||||
{
|
||||
logo: require("../../../static/img/logos/black/anodos.png"),
|
||||
alt: translate("Anodos"),
|
||||
href: "https://anodos.finance/",
|
||||
},
|
||||
{
|
||||
logo: require("../../../static/img/logos/black/sologenic.png"),
|
||||
alt: translate("Sologenic"),
|
||||
href: "https://www.sologenic.com/",
|
||||
},
|
||||
{
|
||||
logo: require("../../../static/img/logos/black/hummingbot.png"),
|
||||
alt: translate("Hummingbot"),
|
||||
href: "https://hummingbot.io/",
|
||||
},
|
||||
{
|
||||
logo: require("../../../static/img/logos/black/first-ledger.png"),
|
||||
alt: translate("First Ledger"),
|
||||
href: "https://firstledger.net/",
|
||||
},
|
||||
{
|
||||
logo: require("../../../static/img/logos/black/strobe.png"),
|
||||
alt: translate("Strobe"),
|
||||
href: "https://strobe.fi/",
|
||||
},
|
||||
{
|
||||
logo: require("../../../static/img/logos/black/securd.png"),
|
||||
alt: translate("Securd"),
|
||||
href: "https://www.securd.finance/",
|
||||
},
|
||||
{
|
||||
logo: require("../../../static/img/logos/black/magnetic.png"),
|
||||
alt: translate("Magnetic"),
|
||||
href: "https://magneticxrpl.com/",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<CardsTextGrid
|
||||
heading={translate("Get Started")}
|
||||
cards={[
|
||||
{
|
||||
heading: translate("Read the Trading Documentation"),
|
||||
description: (
|
||||
<>
|
||||
{translate(
|
||||
"Deep dive into XRPL’s DEX, liquidity pools, and lending protocols."
|
||||
)}
|
||||
<br />
|
||||
<br />
|
||||
<a href="https://xrpl.org/docs/tutorials/how-tos/use-tokens/trade-in-the-decentralized-exchange">
|
||||
{translate("Read the Documentation")}
|
||||
</a>
|
||||
{" • "}
|
||||
<a href="https://discord.gg/sfX3ERAMjH">
|
||||
{translate("Join the Developer Community")}
|
||||
</a>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
heading: translate("Explore Ecosystem Partners"),
|
||||
description: (
|
||||
<>
|
||||
{translate(
|
||||
"Connect with other builders and XRPL engineers. Find tooling, integrations, and liquidity providers."
|
||||
)}
|
||||
<br />
|
||||
<br />
|
||||
<a href="https://xrpl.org/about/uses">
|
||||
{translate("Explore Partners")}
|
||||
</a>
|
||||
</>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
273
index.page.tsx
273
index.page.tsx
@@ -1,257 +1,40 @@
|
||||
import { useThemeHooks } from '@redocly/theme/core/hooks';
|
||||
import { Link } from '@redocly/theme/components/Link/Link';
|
||||
import { BenefitsSection } from 'shared/components/benefits-section';
|
||||
import {
|
||||
HomeBeginJourneySection,
|
||||
HomeBlockchainStatsSection,
|
||||
HomeComplianceDirectorySection,
|
||||
HomeDevelopersFeatureSection,
|
||||
HomeFutureFinanceCallout,
|
||||
HomeHeroCallout,
|
||||
HomeInstitutionsFeatureSection,
|
||||
HomePartnerLogosSection,
|
||||
HomeStayConnectedSection,
|
||||
HomeHero,
|
||||
HomeCarousel,
|
||||
} from "pages/home/sections";
|
||||
|
||||
export const frontmatter = {
|
||||
seo: {
|
||||
title: 'XRP Ledger Home | XRPL.org',
|
||||
description: "XRPL.org is a community-driven site for the XRP Ledger (XRPL), an open-source, public blockchain. Gain access to technical documentation, reference materials, and blockchain ledger tools.",
|
||||
}
|
||||
title: "XRP Ledger Home | XRPL.org",
|
||||
description:
|
||||
"XRPL.org is a community-driven site for the XRP Ledger (XRPL), an open-source, public blockchain. Gain access to technical documentation, reference materials, and blockchain ledger tools.",
|
||||
},
|
||||
};
|
||||
|
||||
const cards = [
|
||||
{
|
||||
id: 'public',
|
||||
title: 'Public and Decentralized',
|
||||
description: 'Open source, open to anyone to build on, maintained by the community',
|
||||
},
|
||||
{
|
||||
id: 'streamlined',
|
||||
title: 'Streamlined Development',
|
||||
description: 'Intentional innovations, tools and documentation reduce time to market',
|
||||
},
|
||||
{ id: 'performance', title: 'High Performance', description: 'Thousands of transactions settled in seconds' },
|
||||
{
|
||||
id: 'low-cost',
|
||||
title: 'Low Cost',
|
||||
description: <>
|
||||
At fractions of a penny per transaction, costs are inexpensive enough to enable a wide variety of <Link to='/about/uses'>blockchain use cases</Link>
|
||||
</>
|
||||
},
|
||||
{
|
||||
id: 'community',
|
||||
title: 'Motivated Community',
|
||||
description: 'Companies, developers, validators, and users work together to make the XRP Ledger better every day',
|
||||
},
|
||||
{
|
||||
id: 'reliability',
|
||||
title: 'Proven Reliability',
|
||||
description: '10+ years of error-free, uninterrupted performance over more than 63 million ledgers',
|
||||
},
|
||||
];
|
||||
|
||||
const cards2 = [
|
||||
{
|
||||
href: '/docs/concepts/tokens/decentralized-exchange/',
|
||||
title: 'Decentralized Exchange',
|
||||
description:
|
||||
'A high-performance decentralized peer-to-peer multi-currency exchange built directly into the blockchain',
|
||||
},
|
||||
{
|
||||
href: '/docs/concepts/payment-types/cross-currency-payments/',
|
||||
title: 'Cross-Currency Payments',
|
||||
description: 'Atomically settle multi-hop payments that cross currency or national boundaries with ease',
|
||||
},
|
||||
{
|
||||
href: '/docs/concepts/payment-types/payment-channels/',
|
||||
title: "Payment Channels",
|
||||
description: 'Batched micropayments with unlimited speed, secured with XRP',
|
||||
},
|
||||
{
|
||||
href: '/docs/concepts/accounts/multi-signing/',
|
||||
title: 'Multi-Signing',
|
||||
description: 'Flexible options for custody and security of on-ledger accounts',
|
||||
},
|
||||
{
|
||||
href: '/docs/concepts/tokens/',
|
||||
title: 'Tokens',
|
||||
description:
|
||||
'All currencies other than XRP can be represented in the XRP Ledger as tokens',
|
||||
},
|
||||
];
|
||||
|
||||
const cards3 = [
|
||||
{
|
||||
href: '/docs/',
|
||||
title: 'Documentation',
|
||||
description: 'Access everything you need to get started working with the XRPL',
|
||||
},
|
||||
{ href: '/docs/tutorials', title: 'Guided Tutorials', description: 'Follow step-by-step guides for frequent tasks' },
|
||||
{ href: '/docs/concepts', title: 'XRPL Fundamentals', description: 'Read about the XRPL\u2019s foundational concepts' },
|
||||
{
|
||||
href: '/docs/references/client-libraries/',
|
||||
title: 'Choose a Language',
|
||||
description: 'Find tools, documentation, and sample code in Python, Java, Javascript, or use HTTP APIs',
|
||||
},
|
||||
{ href: '/about/uses', title: 'Get Inspired', description: 'See what your peers have built on the XRPL' },
|
||||
];
|
||||
|
||||
const features = [
|
||||
{
|
||||
chip: 'In Development',
|
||||
title: 'Smart Contracts',
|
||||
description:
|
||||
<>
|
||||
Hooks are small, efficient WebAssembly modules designed specifically for the XRPL. Check out the <a href='https://hooks-testnet.xrpl-labs.com/' target='_blank'>hooks amendment and public testnet</a> that enable smart contract functionality.
|
||||
</>,
|
||||
href: 'https://hooks-testnet.xrpl-labs.com/',
|
||||
},
|
||||
{
|
||||
chip: 'Enabled',
|
||||
title: 'Automated Market Makers',
|
||||
description: "Smart contracts to provide liquidity and earn passive income from facilitating currency exchange, complementary with the order-book DEX already built into the XRPL.",
|
||||
href: '/docs/concepts/tokens/decentralized-exchange/automated-market-makers/',
|
||||
},
|
||||
];
|
||||
|
||||
export default function Index() {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
return (
|
||||
<div className="landing page-home">
|
||||
<div className="overflow-hidden">
|
||||
<section className="container-new pb-26-until-sm mt-10 mb-10-sm text-center">
|
||||
<div className="w-100">
|
||||
<img id="home-hero-graphic" alt="(stylized X graphic surrounded by a diverse mix of people)" loading='eager' />
|
||||
</div>
|
||||
<div className="col-lg-6 mx-auto text-center pl-0 pr-0">
|
||||
<div className="d-flex flex-column-reverse">
|
||||
<h1 className="mb-10">
|
||||
{translate('home.hero.h1part1', 'The Blockchain')}
|
||||
<br className="until-sm" />
|
||||
{translate('home.hero.h1part2', 'Built for Business')}
|
||||
</h1>
|
||||
<h6 className="eyebrow mb-3">{translate('XRPL | XRP Ledger')}</h6>
|
||||
</div>
|
||||
<Link to="/docs" className="btn btn-primary btn-arrow">
|
||||
{translate('Start Building')}
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
<div className="position-relative d-none-sm">
|
||||
<img src={require('./static/img/backgrounds/home-purple.svg')} id="home-purple" loading="lazy" />
|
||||
<img src={require('./static/img/backgrounds/home-green.svg')} id="home-green" loading="lazy" />
|
||||
</div>
|
||||
<section className="container-new py-26">
|
||||
<div className="col-lg-6 offset-lg-3 pl-0-sm pr-0-sm p-8-sm p-10-until-sm">
|
||||
<h2 className="h4 mb-8 h2-sm">{translate('The XRP Ledger: The Blockchain Built for Business')}</h2>
|
||||
<h6 className="longform mb-10">
|
||||
{translate(
|
||||
'The XRP Ledger (XRPL) is a decentralized, public blockchain led by a global community of businesses and developers looking to solve problems and create value.'
|
||||
)}
|
||||
</h6>
|
||||
<p className="mb-0">
|
||||
{translate(
|
||||
'Proven reliable over more than a decade of error-free functioning, the XRPL offers streamlined development, low transaction costs, high performance, and sustainability. So you can build with confidence–and move your most critical projects forward.'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<BenefitsSection
|
||||
eyebrow="Benefits"
|
||||
title="Why developers choose the XRP Ledger"
|
||||
cards={cards}
|
||||
showImages={true}
|
||||
/>
|
||||
<section className="container-new py-26">
|
||||
<div className="d-flex flex-column-reverse col-sm-8 p-0">
|
||||
<h3 className="h4 h2-sm">
|
||||
{translate(
|
||||
'Activate the proven potential of the XRP Ledger and find a trusted foundation for your next innovation'
|
||||
)}
|
||||
</h3>
|
||||
<h6 className="eyebrow mb-3">{translate('Powerful Features')}</h6>
|
||||
</div>
|
||||
<div className="row row-cols-1 row-cols-lg-3 card-deck mt-10" id="advanced-features">
|
||||
{cards2.map((card, idx) => (
|
||||
<Link className="card" to={card.href} key={card.href + idx}>
|
||||
<div className="card-body">
|
||||
<h4 className="card-title h5">{translate(card.title)}</h4>
|
||||
<p className="card-text">{translate(card.description)}</p>
|
||||
</div>
|
||||
<div className="card-footer"> </div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
<section className="container-new py-26">
|
||||
<div className="d-flex flex-column-reverse col-sm-8 p-0">
|
||||
<h3 className="h4 h2-sm">{translate('Choose a path, and bring your project to life on the XRP Ledger')}</h3>
|
||||
<h6 className="eyebrow mb-3">{translate('Where to Start')}</h6>
|
||||
</div>
|
||||
<div className="row row-cols-1 row-cols-lg-3 card-deck mt-10" id="get-started">
|
||||
{cards3.map((card, idx) => (
|
||||
<Link className="card" to={card.href} key={card.href + idx}>
|
||||
<div className="card-body">
|
||||
<h4 className="card-title h5">{translate(card.title)}</h4>
|
||||
<p className="card-text">{translate(card.description)}</p>
|
||||
</div>
|
||||
<div className="card-footer"> </div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
<section className="container-new py-26">
|
||||
<div className="col-lg-6 offset-lg-3 p-6-sm p-10-until-sm br-8 cta-card">
|
||||
<img src={require('./static/img/backgrounds/cta-home-purple.svg')} className="d-none-sm cta cta-top-left" />
|
||||
<img src={require('./static/img/backgrounds/cta-home-green.svg')} className="cta cta-bottom-right" />
|
||||
<div className="z-index-1 position-relative">
|
||||
<h2 className="h4 mb-8-sm mb-10-until-sm">{translate('Our Shared Vision for XRPL’s Future')}</h2>
|
||||
<p className="mb-10">
|
||||
{translate(
|
||||
"Together, we're building the greenest infrastructure to drive blockchain innovation that doesn't sacrifice utility or performance, to bring the developer community's vision to life."
|
||||
)}
|
||||
</p>
|
||||
<Link className="btn btn-primary btn-arrow" to="/about/">
|
||||
{translate('Learn More')}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section className="container-new py-26">
|
||||
<div className="d-flex flex-column-reverse col-sm-8 p-0">
|
||||
<h3 className="h4 h2-sm">
|
||||
{translate('Explore what the community is building to enable new features and use cases on XRPL')}
|
||||
</h3>
|
||||
<h6 className="eyebrow mb-3">{translate('Preview New Features')}</h6>
|
||||
</div>
|
||||
<ul className="mt-10 card-grid card-grid-3xN">
|
||||
{features.map(feat => (
|
||||
<li className="col ls-none pt-2" key={feat.href}>
|
||||
<Link className="label chip-green" to={feat.href}>
|
||||
{translate(feat.chip)}
|
||||
</Link>
|
||||
<h4 className="mt-3 mb-0 h5">{translate(feat.title)}</h4>
|
||||
<p className="mt-6-until-sm mt-3 mb-0">
|
||||
{typeof feat.description === 'string' ? translate(feat.description) : feat.description}
|
||||
</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
<section className="container-new py-26">
|
||||
<div className="col-md-6 offset-md-3 p-8-sm p-10-until-sm br-8 cta-card">
|
||||
<img alt="" src={require('./static/img/backgrounds/cta-home-magenta.svg')} className="cta cta-bottom-right" />
|
||||
<div className="z-index-1 position-relative">
|
||||
<div className="d-flex flex-column-reverse">
|
||||
<h2 className="h4 mb-8-sm mb-10-until-sm">
|
||||
{translate('Join the Community ')}
|
||||
<br className="until-sm" />
|
||||
{translate(' at XRPL.org')}
|
||||
</h2>
|
||||
</div>
|
||||
<p className="mb-10">
|
||||
{translate('Connect at XRPL.org, a community by and for the developers ')}
|
||||
<br className="until-sm" />
|
||||
{translate(' and entrepreneurs who rely on the XRPL.')}
|
||||
</p>
|
||||
<Link className="btn btn-primary btn-arrow" to="/community">
|
||||
{translate('Get Involved')}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<HomeHero />
|
||||
<HomeHeroCallout />
|
||||
<HomeBlockchainStatsSection />
|
||||
<HomeCarousel />
|
||||
<HomeComplianceDirectorySection />
|
||||
<HomeFutureFinanceCallout />
|
||||
<HomeInstitutionsFeatureSection />
|
||||
<HomeDevelopersFeatureSection />
|
||||
<HomePartnerLogosSection />
|
||||
<HomeBeginJourneySection />
|
||||
<HomeStayConnectedSection />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
2909
package-lock.json
generated
2909
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
20
package.json
20
package.json
@@ -5,8 +5,10 @@
|
||||
"type": "module",
|
||||
"description": "The XRP Ledger Dev Portal is the authoritative source for XRP Ledger documentation, including the `rippled` server, client libraries, and other open-source XRP Ledger software.",
|
||||
"scripts": {
|
||||
"build-css": "sass --load-path styles/scss styles/xrpl.scss ./static/css/devportal2024-v1.css --style compressed --no-source-map",
|
||||
"build-css-watch": "sass --watch --load-path styles/scss styles/xrpl.scss ./static/css/devportal2024-v1.css --style compressed --no-source-map",
|
||||
"analyze-css": "node scripts/analyze-css.js",
|
||||
"build-css": "sass --load-path styles/scss --load-path . styles/xrpl.scss ./static/css/devportal2024-v1.tmp.css && NODE_ENV=production postcss ./static/css/devportal2024-v1.tmp.css -o ./static/css/devportal2024-v1.css && rm -f ./static/css/devportal2024-v1.tmp.css",
|
||||
"build-css:dev": "sass --load-path styles/scss --load-path . styles/xrpl.scss ./static/css/devportal2024-v1.tmp.css --source-map && postcss ./static/css/devportal2024-v1.tmp.css -o ./static/css/devportal2024-v1.css && rm -f ./static/css/devportal2024-v1.tmp.css",
|
||||
"build-css:watch": "sass --watch --load-path styles/scss --load-path . styles/xrpl.scss:static/css/devportal2024-v1.css --source-map",
|
||||
"start": "realm develop"
|
||||
},
|
||||
"keywords": [],
|
||||
@@ -34,11 +36,19 @@
|
||||
},
|
||||
"overrides": {
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0"
|
||||
"react-dom": "^19.1.0",
|
||||
"string-width-cjs": "npm:string-width@^4.2.0",
|
||||
"strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
|
||||
"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bootstrap": "^4.6.2",
|
||||
"@fullhuman/postcss-purgecss": "^7.0.2",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"bootstrap": "^5.3.3",
|
||||
"cssnano": "^7.1.1",
|
||||
"htmltojsx": "^0.3.0",
|
||||
"sass": "1.26.10"
|
||||
"postcss": "^8.5.6",
|
||||
"postcss-cli": "^11.0.1",
|
||||
"sass": "^1.93.2"
|
||||
}
|
||||
}
|
||||
|
||||
141
pages/home/sections/HomeBeginJourneySection.tsx
Normal file
141
pages/home/sections/HomeBeginJourneySection.tsx
Normal file
@@ -0,0 +1,141 @@
|
||||
import { useMemo } from "react";
|
||||
import { useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
|
||||
import type { StandardCardPropsWithoutVariant } from "shared/components/StandardCard";
|
||||
import { BdsLink } from "shared/components/Link";
|
||||
import { StandardCardGroupSection } from "shared/sections/StandardCardGroupSection/StandardCardGroupSection";
|
||||
|
||||
export function HomeBeginJourneySection() {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
const cards = useMemo<readonly StandardCardPropsWithoutVariant[]>(
|
||||
() => [
|
||||
{
|
||||
headline: translate("Documentation"),
|
||||
children: translate(
|
||||
"Access the documentation you need to get started working with the XRPL.",
|
||||
),
|
||||
callsToAction: [
|
||||
{ children: translate("Documentation"), href: "/docs" },
|
||||
] as const,
|
||||
},
|
||||
{
|
||||
headline: translate("Guided Tutorials"),
|
||||
children: translate(
|
||||
"Follow step-by-step tutorials for frequent tasks.",
|
||||
),
|
||||
callsToAction: [
|
||||
{ children: translate("Start Tutorials"), href: "/tutorials" },
|
||||
] as const,
|
||||
},
|
||||
{
|
||||
headline: translate("XRPL Fundamentals"),
|
||||
children: translate(
|
||||
"Read about the XRPL's foundational concepts.",
|
||||
),
|
||||
callsToAction: [
|
||||
{
|
||||
children: translate("Foundational Concepts"),
|
||||
href: "/fundamentals",
|
||||
},
|
||||
] as const,
|
||||
},
|
||||
{
|
||||
headline: translate("Client Libraries"),
|
||||
children: (
|
||||
<span>
|
||||
{translate("Find tools, documentation, and sample code in")}{" "}
|
||||
<BdsLink
|
||||
variant="inline"
|
||||
href="/docs/tutorials/get-started/get-started-python"
|
||||
>
|
||||
{translate("Python")}
|
||||
</BdsLink>
|
||||
,{" "}
|
||||
<BdsLink
|
||||
variant="inline"
|
||||
href="/docs/tutorials/get-started/get-started-java"
|
||||
>
|
||||
{translate("Java")}
|
||||
</BdsLink>
|
||||
,{" "}
|
||||
<BdsLink
|
||||
variant="inline"
|
||||
href="/docs/tutorials/get-started/get-started-javascript?environment=Node"
|
||||
>
|
||||
{translate("JavaScript")}
|
||||
</BdsLink>
|
||||
{translate(", or use")}{" "}
|
||||
<BdsLink
|
||||
variant="inline"
|
||||
href="/docs/tutorials/get-started/get-started-http-websocket-apis"
|
||||
>
|
||||
{translate("HTTP APIs")}
|
||||
</BdsLink>
|
||||
.
|
||||
</span>
|
||||
),
|
||||
callsToAction: [
|
||||
{
|
||||
children: translate("Explore Client Libraries"),
|
||||
href: "/docs/references/client-libraries",
|
||||
},
|
||||
] as const,
|
||||
},
|
||||
{
|
||||
headline: translate("Get Inspired"),
|
||||
children: translate(
|
||||
"See what your peers have built on the XRPL.",
|
||||
),
|
||||
callsToAction: [
|
||||
{
|
||||
children: translate("Built on the XRPL"),
|
||||
href: "/showcase",
|
||||
},
|
||||
] as const,
|
||||
},
|
||||
{
|
||||
headline: translate("XRPL Learning Portal"),
|
||||
children: (
|
||||
<span>
|
||||
{translate("Start with the basics and then learn about")}{" "}
|
||||
<BdsLink variant="inline" href="/docs/use-cases/defi">
|
||||
{translate("DeFi")}
|
||||
</BdsLink>
|
||||
,{" "}
|
||||
<BdsLink variant="inline" href="/docs/use-cases/tokenization">
|
||||
{translate("tokenization")}
|
||||
</BdsLink>
|
||||
,{" "}
|
||||
<BdsLink
|
||||
variant="inline"
|
||||
href="/docs/concepts/tokens/decentralized-exchange"
|
||||
>
|
||||
{translate("DEX")}
|
||||
</BdsLink>{" "}
|
||||
{translate("trading, or how to issue stablecoins.")}
|
||||
</span>
|
||||
),
|
||||
callsToAction: [
|
||||
{
|
||||
children: translate("Learn Now"),
|
||||
href: "/introduction",
|
||||
},
|
||||
] as const,
|
||||
},
|
||||
],
|
||||
[translate],
|
||||
);
|
||||
|
||||
return (
|
||||
<StandardCardGroupSection
|
||||
variant="blue"
|
||||
headline={translate("Begin Your Journey")}
|
||||
description={translate(
|
||||
"XRPL delivers immediate value: faster, cheaper settlement with broad access and full-stack flexibility. XRPL is built for the evolving financial system.",
|
||||
)}
|
||||
cards={cards}
|
||||
/>
|
||||
);
|
||||
}
|
||||
48
pages/home/sections/HomeBlockchainStatsSection.tsx
Normal file
48
pages/home/sections/HomeBlockchainStatsSection.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import { useMemo } from "react";
|
||||
import { useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
|
||||
import { CardStats, CardStatsProps } from "shared/sections/CardStatsList";
|
||||
|
||||
export function HomeBlockchainStatsSection() {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
const blockchainCardStats = useMemo<CardStatsProps["cards"]>(
|
||||
() => [
|
||||
{
|
||||
statistic: translate("12 Years"),
|
||||
label: translate("Continuous uptime"),
|
||||
superscript: "+",
|
||||
variant: "lilac",
|
||||
},
|
||||
{
|
||||
statistic: translate("7M"),
|
||||
label: translate("Active wallets"),
|
||||
superscript: "+",
|
||||
variant: "light-gray",
|
||||
},
|
||||
{
|
||||
statistic: translate("$1T"),
|
||||
label: translate("Value Moved"),
|
||||
superscript: "+",
|
||||
variant: "dark-gray",
|
||||
},
|
||||
{
|
||||
statistic: translate("~$0.00025"),
|
||||
label: translate("Predictable, ultra-low fees"),
|
||||
variant: "green",
|
||||
},
|
||||
],
|
||||
[translate],
|
||||
);
|
||||
|
||||
return (
|
||||
<CardStats
|
||||
heading={translate("Blockchain Trusted at Scale")}
|
||||
description={translate(
|
||||
"Streamline development and build powerful RWA tokenization solutions with XRP Ledger's comprehensive developer toolset.",
|
||||
)}
|
||||
cards={blockchainCardStats}
|
||||
/>
|
||||
);
|
||||
}
|
||||
93
pages/home/sections/HomeCarousel.tsx
Normal file
93
pages/home/sections/HomeCarousel.tsx
Normal file
@@ -0,0 +1,93 @@
|
||||
import React from "react";
|
||||
import { CarouselFeatured } from "shared/patterns/CarouselFeatured";
|
||||
import { useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
|
||||
export const HomeCarousel = () => {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
return (
|
||||
<CarouselFeatured
|
||||
slides={[
|
||||
{
|
||||
id: 0,
|
||||
heading: translate("Built for Finance"),
|
||||
features: [
|
||||
{
|
||||
title: translate("Low Cost, High-Speed:"),
|
||||
description: translate(
|
||||
"Transactions settle in 3-5 seconds for fractions of a cent",
|
||||
),
|
||||
},
|
||||
{
|
||||
title: translate("Compliance-Focused:"),
|
||||
description: translate(
|
||||
"10+ years of enterprise-grade resilience and continuous performance",
|
||||
),
|
||||
},
|
||||
{
|
||||
title: translate("Multi Asset Support:"),
|
||||
description: translate(
|
||||
"From stablecoins to tokenized real-world assets",
|
||||
),
|
||||
},
|
||||
],
|
||||
imageSrc: "/img/home/coin-finance.png",
|
||||
imageAlt: translate("Built for Finance"),
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
heading: translate("Powered by Developers"),
|
||||
features: [
|
||||
{
|
||||
title: translate("$1B+ XRP Grants Program:"),
|
||||
description: translate(
|
||||
"Available for developers and projects building on XRPL",
|
||||
),
|
||||
},
|
||||
{
|
||||
title: translate("Easy-to-Integrate APIs:"),
|
||||
description: translate(
|
||||
"Build with common languages and skip complex smart contract development",
|
||||
),
|
||||
},
|
||||
{
|
||||
title: translate("Full Lifecycle Support:"),
|
||||
description: translate(
|
||||
"From dev tools and testnets to deployment and growth-stage",
|
||||
),
|
||||
},
|
||||
],
|
||||
imageSrc: "/img/home/keyboard-switch.png",
|
||||
imageAlt: translate("Powered by Developers"),
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
heading: translate("Trusted by Institutions"),
|
||||
features: [
|
||||
{
|
||||
title: translate("Utilized by Ripple:"),
|
||||
description: translate(
|
||||
"One of the leading names in enterprise blockchain",
|
||||
),
|
||||
},
|
||||
{
|
||||
title: translate("Institutional-Grade Infrastructure:"),
|
||||
description: translate(
|
||||
"Trusted network of on/off ramps, custodians, and compliance providers",
|
||||
),
|
||||
},
|
||||
{
|
||||
title: translate("70+ Institutional Partners:"),
|
||||
description: translate(
|
||||
"A growing ecosystem of regulated issuers, fintechs, and builders",
|
||||
),
|
||||
},
|
||||
],
|
||||
imageSrc: "/img/home/xrpl-scaffolding.png",
|
||||
imageAlt: translate("Trusted by Institutions"),
|
||||
},
|
||||
]}
|
||||
background="grey"
|
||||
/>
|
||||
);
|
||||
};
|
||||
54
pages/home/sections/HomeComplianceDirectorySection.tsx
Normal file
54
pages/home/sections/HomeComplianceDirectorySection.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import { useMemo } from "react";
|
||||
import { useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
|
||||
import {
|
||||
LinkTextDirectory,
|
||||
LinkTextDirectoryProps,
|
||||
} from "shared/sections/LinkTextDirectory";
|
||||
|
||||
export function HomeComplianceDirectorySection() {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
const developerToolsLinkTextDirectory = useMemo<
|
||||
LinkTextDirectoryProps["cards"]
|
||||
>(
|
||||
() => [
|
||||
{
|
||||
heading: translate("Tokenization"),
|
||||
description: translate(
|
||||
"Tokenization solutions that make it easy to bring financial markets on-chain and enable dynamic, transparent and efficient financing",
|
||||
),
|
||||
buttons: [{ label: translate("Find Out More"), href: "/tokenization" }],
|
||||
},
|
||||
{
|
||||
heading: translate("Payments"),
|
||||
description: translate(
|
||||
"Simple APIs and built-in compliance and custody infrastructure for Fintechs and PSPs to integrate, launch and scale stablecoin payments",
|
||||
),
|
||||
buttons: [{ label: translate("Find Out More"), href: "/payments" }],
|
||||
},
|
||||
{
|
||||
heading: translate("Trading"),
|
||||
description: translate(
|
||||
"Built-in trading infrastructure that blends order book precision with AMM efficiency and gives institutions the efficiency they need, with the control they expect",
|
||||
),
|
||||
buttons: [{ label: translate("Find Out More"), href: "/trading" }],
|
||||
},
|
||||
{
|
||||
heading: translate("Lending (Coming Soon)"),
|
||||
description: translate(
|
||||
"Native lending protocol enabling onchain credit origination with fixed-term, interest-accruing loans, and off-chain underwriting and risk management",
|
||||
),
|
||||
},
|
||||
],
|
||||
[translate],
|
||||
);
|
||||
|
||||
return (
|
||||
<LinkTextDirectory
|
||||
heading={translate("The Compliance-Focused Financial Blockchain")}
|
||||
cards={developerToolsLinkTextDirectory}
|
||||
/>
|
||||
);
|
||||
}
|
||||
29
pages/home/sections/HomeDevelopersFeatureSection.tsx
Normal file
29
pages/home/sections/HomeDevelopersFeatureSection.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import { useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
|
||||
import { FeatureTwoColumn } from "shared/sections/FeatureTwoColumn";
|
||||
|
||||
export function HomeDevelopersFeatureSection() {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
return (
|
||||
<FeatureTwoColumn
|
||||
arrange="right"
|
||||
color="yellow"
|
||||
title={translate("Developers")}
|
||||
description={translate(
|
||||
"Open-source tools, SDKs in multiple languages, and a thriving global community make XRPL the ideal environment to build.",
|
||||
)}
|
||||
media={{
|
||||
src: "/img/home/xrpl-building-developers.jpg",
|
||||
alt: translate("Image of developers using XRPL"),
|
||||
}}
|
||||
links={[
|
||||
{
|
||||
label: translate("Explore the Developer Hub"),
|
||||
href: "/developers",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
19
pages/home/sections/HomeFutureFinanceCallout.tsx
Normal file
19
pages/home/sections/HomeFutureFinanceCallout.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import { useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
|
||||
import { CalloutMediaBanner } from "shared/sections/CalloutMediaBanner";
|
||||
|
||||
export function HomeFutureFinanceCallout() {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
return (
|
||||
<CalloutMediaBanner
|
||||
variant="default"
|
||||
heading={translate("The Future of Finance is Already Onchain")}
|
||||
headingAs="h2"
|
||||
subheading={translate(
|
||||
"XRPL delivers immediate value: faster, cheaper settlement with broad access and full-stack flexibility. XRPL is built for the evolving financial system.",
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
58
pages/home/sections/HomeHero/HomeHero.scss
Normal file
58
pages/home/sections/HomeHero/HomeHero.scss
Normal file
@@ -0,0 +1,58 @@
|
||||
// Spacing tokens - responsive
|
||||
// Note: Uses centralized spacing tokens from _spacing.scss where applicable.
|
||||
// Mobile (<768px)
|
||||
$bds-cmb-gap-mobile: $bds-space-4xl; // 48px - spacing('5xl')
|
||||
$bds-cmb-padding-mobile: $bds-space-lg; // 16px - spacing('lg')
|
||||
|
||||
// Tablet (768px-1023px)
|
||||
$bds-cmb-gap-tablet: $bds-space-6xl; // 64px - spacing('6xl')
|
||||
$bds-cmb-padding-tablet: $bds-space-2xl; // 24px - spacing('2xl')
|
||||
|
||||
// Desktop (≥1024px)
|
||||
$bds-cmb-gap-desktop: $bds-space-8xl; // 80px - spacing('8xl')
|
||||
$bds-cmb-padding-desktop: $bds-space-4xl $bds-space-3xl; // 40px 32px
|
||||
|
||||
.bds-home-hero {
|
||||
&__content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $bds-cmb-gap-mobile;
|
||||
// Mobile-first padding and gap (default)
|
||||
padding: $bds-cmb-padding-mobile;
|
||||
|
||||
// Tablet breakpoint
|
||||
@include media-breakpoint-up(md) {
|
||||
padding: $bds-cmb-padding-tablet;
|
||||
}
|
||||
|
||||
// Desktop breakpoint
|
||||
@include media-breakpoint-up(lg) {
|
||||
padding: $bds-cmb-padding-desktop;
|
||||
}
|
||||
}
|
||||
|
||||
&__header {
|
||||
@include type(display-sm);
|
||||
font-family: $font-family-sans-serif;
|
||||
margin: 0;
|
||||
color: inherit !important;
|
||||
text-align: center;
|
||||
|
||||
.bds-home-hero__subtitle {
|
||||
font-family: $font-family-monospace !important;
|
||||
}
|
||||
}
|
||||
|
||||
&__description {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__media {
|
||||
width: auto;
|
||||
height: auto;
|
||||
display: block;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
35
pages/home/sections/HomeHero/index.tsx
Normal file
35
pages/home/sections/HomeHero/index.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import React from "react";
|
||||
import { PageGrid } from "shared/components/PageGrid/page-grid";
|
||||
|
||||
export function HomeHero() {
|
||||
return (
|
||||
<header className="bds-home-hero">
|
||||
<PageGrid>
|
||||
<PageGrid.Row>
|
||||
<PageGrid.Col span={12}>
|
||||
<div className="bds-home-hero__content">
|
||||
<h1 className="bds-home-hero__header">
|
||||
<span>Built for Finance.</span>
|
||||
<br />
|
||||
<span>Powered by Developers.</span>
|
||||
<br />
|
||||
<span className="bds-home-hero__subtitle">
|
||||
Trusted by Institutions.
|
||||
</span>
|
||||
</h1>
|
||||
<div className="bds-home-hero__description">
|
||||
<img
|
||||
className="bds-home-hero__media"
|
||||
src="/img/home/ripple-icon-timed.png"
|
||||
alt="XRPL home"
|
||||
width={1280}
|
||||
height={458}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</PageGrid.Col>
|
||||
</PageGrid.Row>
|
||||
</PageGrid>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
21
pages/home/sections/HomeHeroCallout.tsx
Normal file
21
pages/home/sections/HomeHeroCallout.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import { useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
|
||||
import { CalloutMediaBanner } from "shared/sections/CalloutMediaBanner";
|
||||
|
||||
export function HomeHeroCallout() {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
return (
|
||||
<CalloutMediaBanner
|
||||
variant="gray"
|
||||
heading={translate("XRP Ledger")}
|
||||
headingAs="h2"
|
||||
backgroundImage={"/img/backgrounds/callout-light.jpg"}
|
||||
subheading={translate(
|
||||
"A decentralized public Layer 1 blockchain for creating, transferring, and exchanging digital assets with a focus on compliance. ",
|
||||
)}
|
||||
buttons={[{ label: translate("Get Started"), href: "/docs" }]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
29
pages/home/sections/HomeInstitutionsFeatureSection.tsx
Normal file
29
pages/home/sections/HomeInstitutionsFeatureSection.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import { useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
|
||||
import { FeatureTwoColumn } from "shared/sections/FeatureTwoColumn";
|
||||
|
||||
export function HomeInstitutionsFeatureSection() {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
return (
|
||||
<FeatureTwoColumn
|
||||
color="green"
|
||||
arrange="left"
|
||||
title={translate("Institutions")}
|
||||
description={translate(
|
||||
"Banks, asset managers, PSPs, and fintechs use XRPL to build financial products and DeFi solutions efficiently and with more flexibility.",
|
||||
)}
|
||||
links={[
|
||||
{
|
||||
label: translate("Explore Institutional Use Cases"),
|
||||
href: "/docs/use-cases/",
|
||||
},
|
||||
]}
|
||||
media={{
|
||||
src: "/img/home/xrpl-building-institutions.jpg",
|
||||
alt: translate("Image of institutions using XRPL"),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
38
pages/home/sections/HomePartnerLogosSection.tsx
Normal file
38
pages/home/sections/HomePartnerLogosSection.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import { useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
import { LogoSquareGrid } from "shared/sections/LogoSquareGrid";
|
||||
|
||||
export function HomePartnerLogosSection() {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
return (
|
||||
<LogoSquareGrid
|
||||
heading={translate("Explore Institutional Use Cases")}
|
||||
variant="gray"
|
||||
buttons={[
|
||||
{
|
||||
label: translate("View All"),
|
||||
href: "/docs/use-cases/",
|
||||
},
|
||||
]}
|
||||
logos={[
|
||||
{
|
||||
logo: "/img/home/ondo-finance.svg",
|
||||
alt: translate("Ondo Finance logo"),
|
||||
},
|
||||
{
|
||||
logo: "/img/home/archax-logo.svg",
|
||||
alt: translate("Archax logo"),
|
||||
},
|
||||
{
|
||||
logo: "/img/home/logo-zonix.svg",
|
||||
alt: translate("Zoniqx logo"),
|
||||
},
|
||||
{
|
||||
logo: "/img/logos/black/zeconomy.png",
|
||||
alt: translate("Zeconomy logo"),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
29
pages/home/sections/HomeStayConnectedSection.tsx
Normal file
29
pages/home/sections/HomeStayConnectedSection.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import { useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
|
||||
import FeatureSingleTopic from "shared/sections/FeatureSingleTopic";
|
||||
|
||||
export function HomeStayConnectedSection() {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
return (
|
||||
<FeatureSingleTopic
|
||||
variant="default"
|
||||
orientation="left"
|
||||
title={translate("Stay Connected")}
|
||||
description={translate(
|
||||
"Join our community and stay in the loop. Get the latest insights on payments, tokenization, trading, and more — straight to your inbox.",
|
||||
)}
|
||||
buttons={[
|
||||
{
|
||||
label: translate("Sign Up to Newsletter"),
|
||||
href: "https://share.hsforms.com/18zNvJDR4QbObGPLDh3n5Bw4vgrs",
|
||||
},
|
||||
]}
|
||||
media={{
|
||||
src: "/img/home/men-review-app.png",
|
||||
alt: translate("Image of a man reviewing an app"),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
11
pages/home/sections/index.ts
Normal file
11
pages/home/sections/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export { HomeHeroCallout } from "./HomeHeroCallout";
|
||||
export { HomeBlockchainStatsSection } from "./HomeBlockchainStatsSection";
|
||||
export { HomeComplianceDirectorySection } from "./HomeComplianceDirectorySection";
|
||||
export { HomeFutureFinanceCallout } from "./HomeFutureFinanceCallout";
|
||||
export { HomeInstitutionsFeatureSection } from "./HomeInstitutionsFeatureSection";
|
||||
export { HomeDevelopersFeatureSection } from "./HomeDevelopersFeatureSection";
|
||||
export { HomePartnerLogosSection } from "./HomePartnerLogosSection";
|
||||
export { HomeBeginJourneySection } from "./HomeBeginJourneySection";
|
||||
export { HomeStayConnectedSection } from "./HomeStayConnectedSection";
|
||||
export { HomeHero } from "./HomeHero";
|
||||
export { HomeCarousel } from "./HomeCarousel";
|
||||
29
pages/payments/sections/AdvancedFeaturesSection.tsx
Normal file
29
pages/payments/sections/AdvancedFeaturesSection.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
import { useThemeHooks } from '@redocly/theme/core/hooks';
|
||||
import FeaturedVideoHero from 'shared/sections/FeaturedVideoHero/FeaturedVideoHero';
|
||||
|
||||
export const AdvancedFeaturesSection: React.FC = () => {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
return (
|
||||
<FeaturedVideoHero
|
||||
headline={translate('Advanced Payment Features')}
|
||||
subtitle={translate(
|
||||
'Helping fintechs and payment providers move money fast, globally, and at low cost - all through simple APIs.',
|
||||
)}
|
||||
video={{
|
||||
source: {
|
||||
type: 'embed',
|
||||
embedUrl: 'https://www.youtube.com/embed/e2Iwsk37LMk',
|
||||
},
|
||||
}}
|
||||
links={[
|
||||
{
|
||||
label: translate('Learn More'),
|
||||
href: 'https://xrpl.org/docs/concepts/payment-types',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
37
pages/payments/sections/DeveloperSpotlightSection.tsx
Normal file
37
pages/payments/sections/DeveloperSpotlightSection.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import React from "react";
|
||||
import { useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
import FeaturedVideoHero from "shared/sections/FeaturedVideoHero/FeaturedVideoHero";
|
||||
|
||||
export const DeveloperSpotlightSection: React.FC = () => {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
return (
|
||||
<FeaturedVideoHero
|
||||
headline={translate("Developer Spotlight")}
|
||||
subtitle={translate(
|
||||
"Are you building a peer-to-peer payments solution, integrating stablecoins, or exploring RLUSD on the XRP Ledger?",
|
||||
)}
|
||||
video={{
|
||||
source: {
|
||||
type: "embed",
|
||||
embedUrl: "https://www.youtube.com/embed/e2Iwsk37LMk",
|
||||
},
|
||||
coverImage: {
|
||||
src: "/img/payments/man-writing.jpg",
|
||||
alt: translate("Developer Spotlight"),
|
||||
},
|
||||
}}
|
||||
links={[
|
||||
{
|
||||
label: translate("Share Your Work"),
|
||||
href: "https://xrpl.org/blog",
|
||||
},
|
||||
{
|
||||
label: translate("View Others"),
|
||||
href: "https://xrplresources.org/developer-spotlight",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
60
pages/payments/sections/EmbeddedPaymentsSection.tsx
Normal file
60
pages/payments/sections/EmbeddedPaymentsSection.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import React from "react";
|
||||
import { useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
import {
|
||||
CardsIconGrid,
|
||||
CardsIconGridProps,
|
||||
} from "shared/sections/CardsIconGrid/CardsIconGrid";
|
||||
|
||||
const EMBEDDED_PAYMENTS_CARDS: CardsIconGridProps["cards"] = [
|
||||
{
|
||||
heading: "Digital Wallets",
|
||||
description:
|
||||
"Offer fast, low-fee stablecoin payments between users and applications.",
|
||||
icon: "/img/payments/filing.svg",
|
||||
},
|
||||
{
|
||||
heading: "Cross-Border Remittance",
|
||||
description:
|
||||
"Use secure payment channels and the most optimal liquidity pathways for global remittances with RLUSD.",
|
||||
icon: "/img/payments/globe-1.svg",
|
||||
},
|
||||
{
|
||||
heading: "Regulated Foreign Exchange",
|
||||
description:
|
||||
"Tap into a set of fiat-backed stablecoins, instantaneous swaps for efficient Foreign Exchange.",
|
||||
icon: "/img/payments/wallet-exchange.svg",
|
||||
},
|
||||
{
|
||||
heading: "Merchant Settlement",
|
||||
description:
|
||||
"Settle daily payments across assets using escrow or checks with compliance-focused features.",
|
||||
icon: "/img/payments/money-hand.svg",
|
||||
},
|
||||
{
|
||||
heading: "B2B Payment Rails",
|
||||
description:
|
||||
"Build programmable payment flows with conditions and real-time data feeds.",
|
||||
icon: "/img/payments/money-hand.svg",
|
||||
},
|
||||
{
|
||||
heading: "Compliance-First Payments",
|
||||
description:
|
||||
"Add Deposit Authorization and whitelisting to comply with AML and KYC workflows.",
|
||||
icon: "/img/payments/checklist.svg",
|
||||
},
|
||||
];
|
||||
|
||||
export const EmbeddedPaymentsSection: React.FC = () => {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
return (
|
||||
<CardsIconGrid
|
||||
heading={translate("Unlock New Business Models with Embedded Payments")}
|
||||
description={translate(
|
||||
"XRPL Payments supports modern fintech use cases with plug-and-play APIs or partner-led deployments.",
|
||||
)}
|
||||
cards={EMBEDDED_PAYMENTS_CARDS}
|
||||
/>
|
||||
);
|
||||
};
|
||||
48
pages/payments/sections/FlexibleIntegrationSection.tsx
Normal file
48
pages/payments/sections/FlexibleIntegrationSection.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import React from 'react';
|
||||
import { useThemeHooks } from '@redocly/theme/core/hooks';
|
||||
import { CardsTextGrid } from 'shared/sections/CardsTextGrid/CardsTextGrid';
|
||||
|
||||
export const FlexibleIntegrationSection: React.FC = () => {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
const cards = [
|
||||
{
|
||||
heading: translate('Build It Yourself'),
|
||||
description: (
|
||||
<>
|
||||
<p>{translate('Ideal for seasoned teams with crypto experience')}</p>
|
||||
<ul>
|
||||
<li>
|
||||
{translate('Access open ')}<a href="https://xrpl.org/docs">{translate('documentation')}</a>
|
||||
</li>
|
||||
<li>
|
||||
{translate('Use the Payments APIs + ')}<a href="https://xrpl.org/resources/dev-tools">{translate('XRPL tooling')}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
heading: translate('Work with a Partner'),
|
||||
description: (
|
||||
<>
|
||||
<p>{translate('Ideal for regulated institutions')}</p>
|
||||
<ul>
|
||||
<li>
|
||||
{translate('Connect with the ')}<a href="https://discord.com/invite/KTNmhJDXqa">{translate('Community')}</a>
|
||||
</li>
|
||||
<li>{translate('Get help for more complex use cases')}</li>
|
||||
</ul>
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<CardsTextGrid
|
||||
heading={translate('Flexible Integration: DIY or Partner-Led')}
|
||||
cards={cards}
|
||||
/>
|
||||
);
|
||||
};
|
||||
23
pages/payments/sections/HeroSection.tsx
Normal file
23
pages/payments/sections/HeroSection.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import React from "react";
|
||||
import { useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
import { HeaderHeroSplitMedia } from "shared/sections/HeaderHeroSplitMedia/HeaderHeroSplitMedia";
|
||||
|
||||
export const HeroSection: React.FC = () => {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
return (
|
||||
<HeaderHeroSplitMedia
|
||||
layout="content-left"
|
||||
title={translate("Payments Infrastructure")}
|
||||
subtitle=""
|
||||
description={translate(
|
||||
"The XRP Ledger Payments Infrastructure is a payments solution for use cases including stablecoin payments, cross-border remittance, B2B payment rails, and merchant settlement.",
|
||||
)}
|
||||
media={{
|
||||
src: "/img/payments/payments-infrastructure-hero.png",
|
||||
alt: translate("Payments Infrastructure"),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
50
pages/payments/sections/PartnerLogosSection.tsx
Normal file
50
pages/payments/sections/PartnerLogosSection.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import React from "react";
|
||||
import { useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
import {
|
||||
LogoSquareGrid,
|
||||
LogoItem,
|
||||
LogoSquareGridProps,
|
||||
} from "shared/sections/LogoSquareGrid/LogoSquareGrid";
|
||||
|
||||
const PARTNER_LOGOS: LogoItem[] = [
|
||||
{
|
||||
logo: "/img/payments/coinpayments.png",
|
||||
alt: "CoinPayments",
|
||||
href: "https://xrpl.org/blog/2025/coinpayments-xrpl-case-study-payment-processing",
|
||||
},
|
||||
{
|
||||
logo: "/img/payments/ripple-blueblack.svg",
|
||||
alt: "Ripple",
|
||||
href: "https://ripple.com/solutions/cross-border-payments/",
|
||||
},
|
||||
{
|
||||
logo: "/img/payments/ffii.svg",
|
||||
alt: "FriiPay",
|
||||
href: "https://xrpl.org/blog/2025/frii-pay-xrpl-case-study-crypto-payment-solution",
|
||||
},
|
||||
{
|
||||
logo: "/img/payments/brale.png",
|
||||
alt: "Brale",
|
||||
href: "https://brale.xyz/blog/brale-goes-live-on-the-xrp-ledger",
|
||||
},
|
||||
{
|
||||
logo: "/img/payments/brazabank.svg",
|
||||
alt: "BrazaBank",
|
||||
href: "https://ripple.com/ripple-press/braza-group-announces-launch-of-bbrl-stablecoin-on-the-xrp-ledger/",
|
||||
},
|
||||
];
|
||||
|
||||
export const PartnerLogosSection: React.FC = () => {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
return (
|
||||
<LogoSquareGrid
|
||||
variant="gray"
|
||||
heading={translate(
|
||||
"Payments Solution, Battle-Tested by Industry Leaders",
|
||||
)}
|
||||
logos={PARTNER_LOGOS}
|
||||
/>
|
||||
);
|
||||
};
|
||||
57
pages/payments/sections/StablecoinsSection.tsx
Normal file
57
pages/payments/sections/StablecoinsSection.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import React from 'react';
|
||||
import { useThemeHooks } from '@redocly/theme/core/hooks';
|
||||
import { StandardCardGroupSection } from 'shared/sections/StandardCardGroupSection/StandardCardGroupSection';
|
||||
import type { StandardCardPropsWithoutVariant } from 'shared/sections/StandardCardGroupSection/StandardCardGroupSection';
|
||||
|
||||
const STABLECOIN_CARDS: readonly StandardCardPropsWithoutVariant[] = [
|
||||
{
|
||||
headline: 'RLUSD',
|
||||
children:
|
||||
"Ripple's enterprise-grade stablecoin, is live on XRPL and fully backed by USD deposits. Built for institutions, it enables fast, compliant, low-cost cross-border payments.",
|
||||
callsToAction: [{ children: 'RLUSD', href: 'https://ripple.com/solutions/stablecoin/' }],
|
||||
},
|
||||
{
|
||||
headline: 'USDC',
|
||||
children:
|
||||
"Issued by Circle, is the world's largest regulated dollar stablecoin and now live on XRPL. It unlocks new corridors for enterprise payments, remittances, and DeFi for millions of users and developers.",
|
||||
callsToAction: [{ children: 'USDC', href: 'https://www.circle.com/usdc' }],
|
||||
},
|
||||
{
|
||||
headline: 'USDB',
|
||||
children:
|
||||
'By Braza Group, is a USD-pegged stablecoin backed by U.S. and Brazilian bonds. Built for FX and remittance, it supports both institutional and retail users via the Braza On app.',
|
||||
callsToAction: [{ children: 'USDB', href: 'https://www.brazabank.com.br/en/usdben/' }],
|
||||
},
|
||||
{
|
||||
headline: 'EUROP',
|
||||
children:
|
||||
'Issued by Schuman Financial, is the first MiCA-compliant euro stablecoin on XRPL. With full reserves at leading EU banks, it brings credible euro liquidity to payments, on-chain FX, and DeFi.',
|
||||
callsToAction: [{ children: 'EUROP', href: 'https://schuman.io/europ/' }],
|
||||
},
|
||||
{
|
||||
headline: 'XSGD',
|
||||
children:
|
||||
"From StraitsX, is a Singapore Dollar-backed stablecoin regulated by MAS. Its launch on XRPL enhances fast, low-cost payments in Southeast Asia's digital economy.",
|
||||
callsToAction: [{ children: 'XSGD', href: 'https://www.straitsx.com/xsgd' }],
|
||||
},
|
||||
{
|
||||
headline: 'AUDD',
|
||||
children:
|
||||
"An Australian dollar stablecoin, is live on XRPL and backed 1:1 with AUD. It supports value transfers, remittance, trade, cross-border transactions and DeFi, enabling AUD utility via XRPL's Decentralised Exchange.",
|
||||
callsToAction: [{ children: 'AUDD', href: 'https://www.audd.digital/' }],
|
||||
},
|
||||
];
|
||||
|
||||
export const StablecoinsSection: React.FC = () => {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
return (
|
||||
<StandardCardGroupSection
|
||||
headline={translate('Enterprise-Grade Stablecoins, Issued Natively on XRPL')}
|
||||
description=""
|
||||
variant="yellow"
|
||||
cards={STABLECOIN_CARDS}
|
||||
/>
|
||||
);
|
||||
};
|
||||
29
pages/payments/sections/StayConnectedSection.tsx
Normal file
29
pages/payments/sections/StayConnectedSection.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import React from "react";
|
||||
import { useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
import { FeatureSingleTopic } from "shared/sections/FeatureSingleTopic/FeatureSingleTopic";
|
||||
|
||||
export const StayConnectedSection: React.FC = () => {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
return (
|
||||
<FeatureSingleTopic
|
||||
orientation="left"
|
||||
title={translate("Stay Connected")}
|
||||
description={translate(
|
||||
"Stay ahead in the world of payments. Subscribe to receive the latest insights, trends, and updates on payment solutions — delivered directly to your inbox.",
|
||||
)}
|
||||
buttons={[
|
||||
{
|
||||
label: translate("Sign Up to Newsletter"),
|
||||
href: "https://share.hsforms.com/18zNvJDR4QbObGPLDh3n5Bw4vgrs",
|
||||
},
|
||||
]}
|
||||
singleButtonVariant="secondary"
|
||||
media={{
|
||||
src: "/img/payments/xrpl-pattern.png",
|
||||
alt: translate("Stay Connected"),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
48
pages/payments/sections/WhyChooseSection.tsx
Normal file
48
pages/payments/sections/WhyChooseSection.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import React from 'react';
|
||||
import { useThemeHooks } from '@redocly/theme/core/hooks';
|
||||
import { LinkTextDirectory } from 'shared/sections/LinkTextDirectory/LinkTextDirectory';
|
||||
|
||||
export const WhyChooseSection: React.FC = () => {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
const cards = [
|
||||
{
|
||||
heading: translate('Enable Cross-Border Stablecoin Payments'),
|
||||
description: (
|
||||
<ul>
|
||||
<li>{translate('Set of regulated stablecoins RLUSD, AUDD, BBRL, USDC etc live on XRPL')}</li>
|
||||
<li>{translate('Easily receive, store, convert, issue and send stablecoins')}</li>
|
||||
</ul>
|
||||
),
|
||||
buttons: [],
|
||||
},
|
||||
{
|
||||
heading: translate('Access Reliable Payments Infrastructure'),
|
||||
description: (
|
||||
<ul>
|
||||
<li>{translate('Uninterrupted performance with 99.9% uptime since 2012')}</li>
|
||||
<li>{translate('Over $1T+ in value moving transactions processed to date')}</li>
|
||||
</ul>
|
||||
),
|
||||
buttons: [],
|
||||
},
|
||||
{
|
||||
heading: translate('Move Money Efficiently'),
|
||||
description: (
|
||||
<ul>
|
||||
<li>{translate('Transactions settle atomically, in 3-5 seconds')}</li>
|
||||
<li>{translate('Predictable and ultra-low transaction fees')}</li>
|
||||
</ul>
|
||||
),
|
||||
buttons: [],
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<LinkTextDirectory
|
||||
heading={translate('Why Choose XRPL Payments Suite for Your Payment Rails?')}
|
||||
cards={cards}
|
||||
/>
|
||||
);
|
||||
};
|
||||
9
pages/payments/sections/index.ts
Normal file
9
pages/payments/sections/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export { HeroSection } from './HeroSection';
|
||||
export { WhyChooseSection } from './WhyChooseSection';
|
||||
export { AdvancedFeaturesSection } from './AdvancedFeaturesSection';
|
||||
export { StablecoinsSection } from './StablecoinsSection';
|
||||
export { EmbeddedPaymentsSection } from './EmbeddedPaymentsSection';
|
||||
export { PartnerLogosSection } from './PartnerLogosSection';
|
||||
export { FlexibleIntegrationSection } from './FlexibleIntegrationSection';
|
||||
export { DeveloperSpotlightSection } from './DeveloperSpotlightSection';
|
||||
export { StayConnectedSection } from './StayConnectedSection';
|
||||
161
postcss.config.cjs
Normal file
161
postcss.config.cjs
Normal file
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* PostCSS Configuration
|
||||
*
|
||||
* Processes compiled Sass output through:
|
||||
* 1. PurgeCSS - Removes unused CSS selectors
|
||||
* 2. Autoprefixer - Adds vendor prefixes for browser compatibility
|
||||
* 3. cssnano - Minifies and optimizes CSS (production only)
|
||||
*/
|
||||
|
||||
const purgecss = require('@fullhuman/postcss-purgecss').default;
|
||||
const autoprefixer = require('autoprefixer');
|
||||
const cssnano = require('cssnano');
|
||||
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
// Only run PurgeCSS in production or when explicitly enabled
|
||||
...(isProduction || process.env.PURGECSS === 'true'
|
||||
? [
|
||||
purgecss({
|
||||
// Scan all content files for class names (TSX/TS/HTML = usage; SCSS = BEM modifier definitions)
|
||||
content: [
|
||||
'./**/*.tsx',
|
||||
'./**/*.ts',
|
||||
'./**/*.md',
|
||||
'./**/*.yaml',
|
||||
'./**/*.html',
|
||||
'./**/*.scss',
|
||||
'./static/js/**/*.js',
|
||||
'./static/vendor/**/*.js',
|
||||
// Ignore node_modules except for specific libraries that inject classes
|
||||
'!./node_modules/**/*',
|
||||
],
|
||||
|
||||
// Default extractor - looks for class names in content
|
||||
defaultExtractor: content => {
|
||||
// Match all words, including those with dashes and numbers
|
||||
const broadMatches = content.match(/[^<>"'`\s]*[^<>"'`\s:]/g) || [];
|
||||
// Match class names in className="..." or class="..."
|
||||
const classMatches = content.match(/(?:class|className)=["']([^"']*)["']/g) || [];
|
||||
const classes = classMatches.flatMap(match => {
|
||||
const m = match.match(/["']([^"']*)["']/);
|
||||
return m ? m[1].split(/\s+/) : [];
|
||||
});
|
||||
// Extract BEM modifier classes from SCSS (e.g. .bds-callout-media-banner--green)
|
||||
// These are dynamically applied in TSX via template literals so PurgeCSS won't find them as literals
|
||||
const scssBemMatches = content.match(/\.(bds-[a-z0-9-]+--[a-z0-9-]+)/g) || [];
|
||||
const bemModifiers = [...new Set(scssBemMatches.map(m => m.replace(/^\./, '')))];
|
||||
return [...new Set([...broadMatches, ...classes, ...bemModifiers])];
|
||||
},
|
||||
|
||||
// Safelist - classes that should never be removed
|
||||
safelist: {
|
||||
// Standard safelist - dynamic state classes
|
||||
standard: [
|
||||
'html',
|
||||
'body',
|
||||
'light',
|
||||
'dark',
|
||||
'show',
|
||||
'hide',
|
||||
'active',
|
||||
'disabled',
|
||||
'open',
|
||||
'collapsed',
|
||||
'collapsing',
|
||||
'lang-ja', // Japanese language class
|
||||
/^lang-/, // All language classes
|
||||
// Common Bootstrap utility patterns that should always be kept
|
||||
/^container/, // All container classes
|
||||
/^row$/, // Row class
|
||||
/^col-/, // Column classes
|
||||
/^bds-grid__col/, // PageGrid column classes (dynamic span values)
|
||||
/^bds-grid__offset/, // PageGrid offset classes
|
||||
// BDS BEM modifier classes - applied dynamically via template literals (e.g. bds-callout-media-banner--green)
|
||||
// Required: PurgeCSS cannot find these in TSX/SCSS due to interpolation
|
||||
/^bds-[a-z0-9-]+--/,
|
||||
/^g-/, // Gap utilities
|
||||
/^p-/, // Padding utilities
|
||||
/^m-/, // Margin utilities
|
||||
/^px-/, /^py-/, /^pt-/, /^pb-/, /^ps-/, /^pe-/, // Directional padding
|
||||
/^mx-/, /^my-/, /^mt-/, /^mb-/, /^ms-/, /^me-/, // Directional margin
|
||||
/^d-/, // Display utilities
|
||||
/^flex-/, // Flexbox utilities
|
||||
/^justify-/, // Justify content
|
||||
/^align-/, // Align items
|
||||
/^w-/, // Width utilities
|
||||
/^h-/, // Height utilities
|
||||
/^text-/, // Text utilities
|
||||
/^bg-/, // Background utilities
|
||||
/^border/, // Border utilities
|
||||
/^rounded/, // Border radius
|
||||
],
|
||||
|
||||
// Deep safelist - MINIMAL - only truly dynamic components
|
||||
deep: [
|
||||
// Bootstrap JS components (only if actually used with JS)
|
||||
/dropdown-menu/,
|
||||
/dropdown-item/,
|
||||
/modal-backdrop/,
|
||||
/fade/,
|
||||
|
||||
// Third-party libraries
|
||||
/cm-/,
|
||||
/CodeMirror/,
|
||||
/lottie/,
|
||||
],
|
||||
|
||||
// Greedy safelist - VERY MINIMAL
|
||||
greedy: [
|
||||
/data-theme/, // Theme switching
|
||||
],
|
||||
},
|
||||
|
||||
// Reject specific patterns - don't remove these even if not found
|
||||
rejected: [],
|
||||
|
||||
// Variables - keep CSS custom properties
|
||||
variables: true,
|
||||
|
||||
// Keyframes - keep animation keyframes
|
||||
keyframes: true,
|
||||
|
||||
// Font-face rules
|
||||
fontFace: true,
|
||||
}),
|
||||
]
|
||||
: []),
|
||||
|
||||
// Autoprefixer - adds vendor prefixes
|
||||
autoprefixer({
|
||||
overrideBrowserslist: [
|
||||
'>0.2%',
|
||||
'not dead',
|
||||
'not op_mini all',
|
||||
'last 2 versions',
|
||||
],
|
||||
}),
|
||||
|
||||
// cssnano - minification (production only)
|
||||
...(isProduction
|
||||
? [
|
||||
cssnano({
|
||||
preset: [
|
||||
'default',
|
||||
{
|
||||
discardComments: {
|
||||
removeAll: true,
|
||||
},
|
||||
normalizeWhitespace: true,
|
||||
colormin: true,
|
||||
minifySelectors: true,
|
||||
},
|
||||
],
|
||||
}),
|
||||
]
|
||||
: []),
|
||||
],
|
||||
};
|
||||
|
||||
@@ -54,16 +54,12 @@ scripts:
|
||||
head:
|
||||
- src: https://cmp.osano.com/AzyjT6TIZMlgyLyy8/f11f7772-8ed5-4b73-bd17-c0814edcc440/osano.js
|
||||
- src: ./static/js/xrpl-2.11.0.min.js
|
||||
- src: ./static/vendor/jquery-3.7.1.min.js
|
||||
# - src: ./static/vendor/jquery-3.7.1.min.js
|
||||
- src: ./static/vendor/bootstrap.min.js
|
||||
- src: ./static/js/osano.js
|
||||
type: text/javascript
|
||||
links:
|
||||
- href: https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700&display=swap
|
||||
rel: stylesheet
|
||||
- href: https://fonts.googleapis.com/css?family=Source+Code+Pro:300,400,600,700&display=swap
|
||||
rel: stylesheet
|
||||
- href: https://fonts.googleapis.com/css?family=Space+Grotesk:300,400,600,700&display=swap
|
||||
- href: https://fonts.googleapis.com/css2?family=Noto+Sans:wght@300;400;500;600;700&family=Noto+Serif:wght@400;500;600;700&family=Noto+Sans+JP:wght@300;400;500;600;700&display=swap
|
||||
rel: stylesheet
|
||||
- href: ./static/css/devportal2024-v1.css
|
||||
rel: stylesheet
|
||||
|
||||
@@ -40,25 +40,17 @@ export default function CodeSamples() {
|
||||
{/* <a className="mt-12 btn btn-primary btn-arrow">Submit Code Samples</a> */}
|
||||
</div>
|
||||
</section>
|
||||
<div className="position-relative d-none-sm">
|
||||
<img
|
||||
alt="orange waves"
|
||||
src={require('../static/img/backgrounds/xrpl-overview-orange.svg')}
|
||||
id="xrpl-overview-orange"
|
||||
/>
|
||||
</div>
|
||||
<section className="container-new py-26">
|
||||
<div className="d-flex flex-column col-sm-8 p-0">
|
||||
<h3 className="h4 h2-sm">
|
||||
{translate('Browse sample code for building common use cases on the XRP Ledger')}
|
||||
</h3>
|
||||
</div>
|
||||
<div className="row col-12 card-deck mt-10" id="code-samples-deck">
|
||||
<div className="row col-md-12 px-0" id="code_samples_list">
|
||||
{codeSamples.map(card => (
|
||||
<div className="row gx-4 gy-5 mt-10 mb-20" id="code-samples-deck">
|
||||
{codeSamples.map(card => (
|
||||
<div key={card.href} className="col-12 col-lg-6 mb-4">
|
||||
<a
|
||||
key={card.href}
|
||||
className={`card cardtest col-12 col-lg-5 ${card.langs.join(' ')}`}
|
||||
className={`card cardtest h-100 ${card.langs.join(' ')}`}
|
||||
href={target.github_forkurl + `/tree/${target.github_branch}/${card.href}`.replace('/content','')}
|
||||
>
|
||||
<div className="card-header">
|
||||
@@ -72,10 +64,9 @@ export default function CodeSamples() {
|
||||
<h4 className="card-title h5">{card.title}</h4>
|
||||
<p className="card-text">{card.description}</p>
|
||||
</div>
|
||||
<div className="card-footer"> </div>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
<section className="container-new py-26">
|
||||
@@ -86,8 +77,8 @@ export default function CodeSamples() {
|
||||
{translate('Help the XRPL community by submitting your own code samples')}
|
||||
</h6>
|
||||
</div>
|
||||
<div className="row pl-4">
|
||||
<div className=" col-lg-3 pl-4 pl-lg-0 pr-4 contribute dot contribute_1">
|
||||
<div className="row ps-4">
|
||||
<div className=" col-lg-3 ps-4 ps-lg-0 pe-4 contribute dot contribute_1">
|
||||
<span className="dot" />
|
||||
<h5 className="pb-4 pt-md-5">{translate('Fork and clone')}</h5>
|
||||
<p className="pb-4">
|
||||
@@ -98,7 +89,7 @@ export default function CodeSamples() {
|
||||
{translate('resources.contribute.1.part3', '. Using git, clone the fork to your computer.')}
|
||||
</p>
|
||||
</div>
|
||||
<div className=" col-lg-3 pl-4 pl-lg-0 pr-4 contribute dot contribute_2">
|
||||
<div className=" col-lg-3 ps-4 ps-lg-0 pe-4 contribute dot contribute_2">
|
||||
<span className="dot" />
|
||||
<h5 className="pb-4 pt-md-5">{translate('Add to folder')}</h5>
|
||||
<p className="pb-4">
|
||||
|
||||
@@ -62,8 +62,8 @@ export function CurlButton ({selectedConnection, currentBody}: CurlButtonProps)
|
||||
return <>
|
||||
<button
|
||||
className="btn btn-outline-secondary curl"
|
||||
data-toggle="modal"
|
||||
data-target="#wstool-1-curl"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#wstool-1-curl"
|
||||
title={translate("cURL Syntax")}
|
||||
onClick={() => setShowCurlModal(true)}
|
||||
>
|
||||
|
||||
@@ -62,8 +62,8 @@ export function PermalinkButton ({currentBody, selectedConnection}: PermaLinkBut
|
||||
return <>
|
||||
<button
|
||||
className="btn btn-outline-secondary permalink"
|
||||
data-toggle="modal"
|
||||
data-target="#wstool-1-permalink"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#wstool-1-permalink"
|
||||
title={translate("Permalink")}
|
||||
onClick={() => setShowPermalinkModal(true)}
|
||||
>
|
||||
|
||||
@@ -146,8 +146,8 @@ export default function DevTools() {
|
||||
<button
|
||||
className="nav-link active dev-tools-tab"
|
||||
id="explorers-tab"
|
||||
data-toggle="tab"
|
||||
data-target="#explorers"
|
||||
data-bs-toggle="tab"
|
||||
data-bs-target="#explorers"
|
||||
role="tab"
|
||||
aria-controls="explorers"
|
||||
aria-selected="true"
|
||||
@@ -159,8 +159,8 @@ export default function DevTools() {
|
||||
<button
|
||||
className="nav-link dev-tools-tab"
|
||||
id="api-access-tab"
|
||||
data-toggle="tab"
|
||||
data-target="#api-access"
|
||||
data-bs-toggle="tab"
|
||||
data-bs-target="#api-access"
|
||||
role="tab"
|
||||
aria-controls="api-access"
|
||||
aria-selected="false"
|
||||
@@ -172,8 +172,8 @@ export default function DevTools() {
|
||||
<button
|
||||
className="nav-link dev-tools-tab"
|
||||
id="other-tab"
|
||||
data-toggle="tab"
|
||||
data-target="#other"
|
||||
data-bs-toggle="tab"
|
||||
data-bs-target="#other"
|
||||
role="tab"
|
||||
aria-controls="other"
|
||||
aria-selected="false"
|
||||
@@ -278,16 +278,6 @@ export default function DevTools() {
|
||||
</section>
|
||||
<section className="container-new py-10 px-0">
|
||||
<div className="col-lg-12 p-6-sm p-10-until-sm br-8 cta-card">
|
||||
<img
|
||||
alt="purple waves"
|
||||
src={require("../../static/img/backgrounds/cta-home-purple.svg")}
|
||||
className="d-none-sm cta cta-top-left"
|
||||
/>
|
||||
<img
|
||||
alt="green waves"
|
||||
src={require("../../static/img/backgrounds/cta-home-green.svg")}
|
||||
className="cta cta-bottom-right"
|
||||
/>
|
||||
<div className="z-index-1 position-relative">
|
||||
<h2 className="h4 mb-8-sm mb-10-until-sm">
|
||||
{translate("Have an Idea For a Tool?")}
|
||||
|
||||
@@ -251,7 +251,7 @@ export function WebsocketApiTool() {
|
||||
className="btn-toolbar justify-content-between pt-4"
|
||||
role="toolbar"
|
||||
>
|
||||
<div className="btn-group mr-3" role="group">
|
||||
<div className="btn-group me-3" role="group">
|
||||
<button
|
||||
className="btn btn-outline-secondary send-request"
|
||||
onClick={() => sendWebSocketMessage(currentBody)}
|
||||
@@ -272,8 +272,8 @@ export function WebsocketApiTool() {
|
||||
connected ? "btn-success" : "btn-outline-secondary"
|
||||
} ${connectionError ?? "btn-danger"}`}
|
||||
onClick={openConnectionModal}
|
||||
data-toggle="modal"
|
||||
data-target="#wstool-1-connection-settings"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#wstool-1-connection-settings"
|
||||
>
|
||||
{`${selectedConnection.shortname}${
|
||||
connected ? ` (${translate('Connected')})` : ` (${translate('Not Connected')})`
|
||||
|
||||
@@ -184,7 +184,7 @@ function TestCredentials({selectedFaucet, translate}) {
|
||||
setBalance,
|
||||
setSequence,
|
||||
translate)
|
||||
} className="btn btn-primary mr-2 mb-2">
|
||||
} className="btn btn-primary me-2 mb-2">
|
||||
{`${translate('resources.dev-tools.faucet.cred-btn.part1', 'Generate ')}${selectedFaucet.shortName}${translate('resources.dev-tools.faucet.cred-btn.part2', ' credentials')}`}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
html: resources.html
|
||||
parent: index.html
|
||||
metadata:
|
||||
indexPage: true
|
||||
---
|
||||
# Resources
|
||||
|
||||
Other resources to help understand the XRP Ledger and develop on it.
|
||||
|
||||
|
||||
{% child-pages /%}
|
||||
246
resources/index.page.tsx
Normal file
246
resources/index.page.tsx
Normal file
@@ -0,0 +1,246 @@
|
||||
import { useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
import { HeaderHeroSplitMedia } from "shared/sections/HeaderHeroSplitMedia/HeaderHeroSplitMedia";
|
||||
import { CardsFeatured } from "shared/sections/CardsFeatured/CardsFeatured";
|
||||
import { LinkTextDirectory } from "shared/sections/LinkTextDirectory/LinkTextDirectory";
|
||||
import { FeatureTwoColumn } from "shared/sections/FeatureTwoColumn/FeatureTwoColumn";
|
||||
import { FeatureSingleTopic } from "shared/sections/FeatureSingleTopic/FeatureSingleTopic";
|
||||
|
||||
export const frontmatter = {
|
||||
seo: {
|
||||
title: "XRPL Developer Resources",
|
||||
description:
|
||||
"Whether you’re exploring the XRP Ledger for the first time or scaling an enterprise solution, find essential blockchain and crypto resources to build, learn, and stay ahead.",
|
||||
},
|
||||
};
|
||||
|
||||
const HUBSPOT_NEWSLETTER_FORM =
|
||||
"https://share.hsforms.com/18zNvJDR4QbObGPLDh3n5Bw4vgrs";
|
||||
|
||||
export default function Resources() {
|
||||
const { useTranslate } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
return (
|
||||
<div className="landing">
|
||||
<HeaderHeroSplitMedia
|
||||
title={translate("Everything you need in One Place.")}
|
||||
subtitle={translate(
|
||||
"Whether you’re exploring the XRP Ledger for the first time or scaling an enterprise solution, here you'll find the essential blockchain and crypto resources developers need to build, learn, and stay ahead."
|
||||
)}
|
||||
primaryCta={{
|
||||
label: translate("Explore Solutions"),
|
||||
href: "http://xrpl.org/develop",
|
||||
}}
|
||||
media={{
|
||||
src: require("../static/img/bds-2026/resources-hero-media.jpg"),
|
||||
alt: translate("Everything you need in One Place."),
|
||||
}}
|
||||
/>
|
||||
|
||||
<CardsFeatured
|
||||
heading={translate("Start Building")}
|
||||
description={translate(
|
||||
"Get up to speed with a comprehensive set of blockchain resources, including the tech, tools, and documentation that power the XRPL ecosystem."
|
||||
)}
|
||||
cards={[
|
||||
{
|
||||
image: require("../static/img/bds-2026/resources-feature-media-1.jpg"),
|
||||
imageAlt: translate("Documentation"),
|
||||
title: translate("Documentation"),
|
||||
subtitle: translate(
|
||||
"Access everything you need to get started on the XRPL."
|
||||
),
|
||||
buttonLabel: translate("Get Started"),
|
||||
href: "https://xrpl.org/docs",
|
||||
},
|
||||
{
|
||||
image: require("../static/img/bds-2026/resources-feature-media-2.jpg"),
|
||||
imageAlt: translate("Guided Tutorials"),
|
||||
title: translate("Guided Tutorials"),
|
||||
subtitle: translate(
|
||||
"Follow step-by-step guides for frequent tasks."
|
||||
),
|
||||
buttonLabel: translate("Guides"),
|
||||
href: "https://xrpl.org/docs/tutorials",
|
||||
},
|
||||
{
|
||||
image: require("../static/img/bds-2026/resources-feature-media-3.jpg"),
|
||||
imageAlt: translate("Fundamentals"),
|
||||
title: translate("Fundamentals"),
|
||||
subtitle: translate(
|
||||
"Read about the XRPL's foundational concepts."
|
||||
),
|
||||
buttonLabel: translate("Fundamentals"),
|
||||
href: "https://xrpl.org/docs/introduction",
|
||||
},
|
||||
{
|
||||
image: require("../static/img/bds-2026/resources-feature-media-4.jpg"),
|
||||
imageAlt: translate("Languages"),
|
||||
title: translate("Languages"),
|
||||
subtitle: translate(
|
||||
"Find tools, documentation, and sample code in Python, Java, JavaScript, or use HTTP APIs."
|
||||
),
|
||||
buttonLabel: translate("Languages"),
|
||||
href: "https://xrpl.org/docs/tutorials/python",
|
||||
},
|
||||
{
|
||||
image: require("../static/img/bds-2026/resources-feature-media-5.jpg"),
|
||||
imageAlt: translate("GitHub"),
|
||||
title: translate("GitHub"),
|
||||
subtitle: translate(
|
||||
"Dive into the open-source codebase and track new developments."
|
||||
),
|
||||
buttonLabel: translate("GitHub"),
|
||||
href: "https://github.com/XRPLF/rippled",
|
||||
},
|
||||
{
|
||||
image: require("../static/img/bds-2026/community-feature-media-2.jpg"),
|
||||
imageAlt: translate("Learning Portal"),
|
||||
title: translate("Learning Portal"),
|
||||
subtitle: translate(
|
||||
"Start with the basics and then learn about tokenization, DEX trading, or how to issue stablecoins."
|
||||
),
|
||||
buttonLabel: translate("Tokenization"),
|
||||
href: "https://learn.xrpl.org/course/tokenization-and-real-world-assets-on-the-xrpl/",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<LinkTextDirectory
|
||||
heading={translate("Explore XRPL Developer Tools")}
|
||||
description={translate(
|
||||
"Community-built tools to make development easier and faster"
|
||||
)}
|
||||
cards={[
|
||||
{
|
||||
heading: translate("Ecosystem Directory"),
|
||||
description: translate(
|
||||
"Discover XRPL tools, integrations, and community projects."
|
||||
),
|
||||
buttons: [
|
||||
{
|
||||
label: translate("Ecosystem"),
|
||||
href: "https://xrpl.org/about/uses",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: translate("XRPL Explorer"),
|
||||
description: translate(
|
||||
"Track transactions and activity on the ledger in real time."
|
||||
),
|
||||
buttons: [
|
||||
{
|
||||
label: translate("Explorer"),
|
||||
href: "https://livenet.xrpl.org/",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: translate("Faucet & Testnet Tools"),
|
||||
description: translate(
|
||||
"Access devnet and testnet environments to prototype and test."
|
||||
),
|
||||
buttons: [
|
||||
{
|
||||
label: translate("Testnet"),
|
||||
href: "https://testnet.xrpl.org/",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: translate("Reference Implementations"),
|
||||
description: translate(
|
||||
"Get started with example projects and open templates."
|
||||
),
|
||||
buttons: [
|
||||
{
|
||||
label: translate("References"),
|
||||
href: "https://xrpl.org/docs/references",
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<FeatureTwoColumn
|
||||
color="green"
|
||||
arrange="left"
|
||||
title={translate("XRPL Grants & Accelerator")}
|
||||
description={translate(
|
||||
"Apply for funding and guidance to bring your ideas to life and help them scale."
|
||||
)}
|
||||
links={[
|
||||
{
|
||||
label: translate("Funding and Guidance"),
|
||||
href: "https://xrplgrants.org/",
|
||||
},
|
||||
]}
|
||||
media={{
|
||||
src: require("../static/img/bds-2026/resources-feature-media-7.jpg"),
|
||||
alt: translate("XRPL Grants & Accelerator"),
|
||||
}}
|
||||
/>
|
||||
|
||||
<FeatureTwoColumn
|
||||
color="green"
|
||||
arrange="right"
|
||||
title={translate("XRPL Commons")}
|
||||
description={translate(
|
||||
"Utilize the Core Dev Bootcamp to level up your blockchain journey and join The Aquarium Residency if you’re ready for launch."
|
||||
)}
|
||||
links={[
|
||||
{
|
||||
label: translate("Core Dev Bootcamp"),
|
||||
href: "https://www.xrpl-commons.org/build/core-dev-online-bootcamp",
|
||||
},
|
||||
{
|
||||
label: translate("The Aquarium Residency"),
|
||||
href: "https://www.xrpl-commons.org/residency",
|
||||
},
|
||||
]}
|
||||
media={{
|
||||
src: require("../static/img/bds-2026/resources-feature-media-8.jpg"),
|
||||
alt: translate("XRPL Commons"),
|
||||
}}
|
||||
/>
|
||||
|
||||
<FeatureTwoColumn
|
||||
color="green"
|
||||
arrange="left"
|
||||
title={translate("Hackathons & Events")}
|
||||
description={translate(
|
||||
"Compete, connect, and contribute at upcoming global developer events."
|
||||
)}
|
||||
links={[
|
||||
{
|
||||
label: translate("Global Developer Events"),
|
||||
href: "https://xrpl.org/community/events",
|
||||
},
|
||||
]}
|
||||
media={{
|
||||
src: require("../static/img/bds-2026/resources-feature-media-9.jpg"),
|
||||
alt: translate("Hackathons & Events"),
|
||||
}}
|
||||
/>
|
||||
|
||||
<FeatureSingleTopic
|
||||
orientation="left"
|
||||
title={translate("Building on XRPL Starts Here")}
|
||||
description={translate(
|
||||
"From docs to funding and beyond, you’ll find all the crypto resources you need to take your project further."
|
||||
)}
|
||||
buttons={[
|
||||
{
|
||||
label: translate("Sign Up to Newsletter"),
|
||||
href: HUBSPOT_NEWSLETTER_FORM,
|
||||
},
|
||||
]}
|
||||
media={{
|
||||
src: require("../static/img/bds-2026/resources-feature-media-10.jpg"),
|
||||
alt: translate("Building on XRPL Starts Here"),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
166
scripts/analyze-css.js
Normal file
166
scripts/analyze-css.js
Normal file
@@ -0,0 +1,166 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* CSS Analysis Script
|
||||
*
|
||||
* Analyzes the compiled CSS bundle to identify:
|
||||
* - Total size and line count
|
||||
* - Bootstrap vs custom CSS breakdown
|
||||
* - Most common selectors and patterns
|
||||
* - Potential optimization opportunities
|
||||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
const CSS_FILE = path.join(__dirname, '../static/css/devportal2024-v1.css');
|
||||
|
||||
function analyzeCSS() {
|
||||
console.log('🔍 Analyzing CSS Bundle...\n');
|
||||
|
||||
if (!fs.existsSync(CSS_FILE)) {
|
||||
console.error(`❌ CSS file not found: ${CSS_FILE}`);
|
||||
console.log('💡 Run "npm run build-css" first to generate the CSS bundle.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const css = fs.readFileSync(CSS_FILE, 'utf-8');
|
||||
const stats = fs.statSync(CSS_FILE);
|
||||
|
||||
// Basic stats
|
||||
const lines = css.split('\n').length;
|
||||
const sizeKB = (stats.size / 1024).toFixed(2);
|
||||
const selectors = css.match(/[^{}]+(?=\{)/g) || [];
|
||||
const uniqueSelectors = new Set(selectors.map(s => s.trim())).size;
|
||||
|
||||
console.log('📊 Bundle Statistics:');
|
||||
console.log('━'.repeat(50));
|
||||
console.log(` Size: ${sizeKB} KB`);
|
||||
console.log(` Lines: ${lines.toLocaleString()}`);
|
||||
console.log(` Total Selectors: ${selectors.length.toLocaleString()}`);
|
||||
console.log(` Unique Selectors: ${uniqueSelectors.toLocaleString()}`);
|
||||
console.log('');
|
||||
|
||||
// Bootstrap component detection
|
||||
const bootstrapPatterns = {
|
||||
'Grid System': /\.(container|row|col-)/g,
|
||||
'Buttons': /\.btn-/g,
|
||||
'Forms': /\.(form-control|form-select|form-check)/g,
|
||||
'Cards': /\.card-/g,
|
||||
'Navbar': /\.navbar-/g,
|
||||
'Dropdown': /\.dropdown-/g,
|
||||
'Modal': /\.modal-/g,
|
||||
'Alert': /\.alert-/g,
|
||||
'Badge': /\.badge-/g,
|
||||
'Breadcrumb': /\.breadcrumb/g,
|
||||
'Pagination': /\.page-/g,
|
||||
'Accordion': /\.accordion/g,
|
||||
'Carousel': /\.carousel/g,
|
||||
'Tooltip': /\.tooltip/g,
|
||||
'Popover': /\.popover/g,
|
||||
'Toast': /\.toast/g,
|
||||
'Spinner': /\.spinner-/g,
|
||||
};
|
||||
|
||||
console.log('🎨 Bootstrap Component Usage:');
|
||||
console.log('━'.repeat(50));
|
||||
|
||||
const componentUsage = [];
|
||||
for (const [component, pattern] of Object.entries(bootstrapPatterns)) {
|
||||
const matches = css.match(pattern);
|
||||
const count = matches ? matches.length : 0;
|
||||
componentUsage.push({ component, count });
|
||||
}
|
||||
|
||||
componentUsage.sort((a, b) => b.count - a.count);
|
||||
componentUsage.forEach(({ component, count }) => {
|
||||
const bar = '█'.repeat(Math.min(Math.floor(count / 10), 40));
|
||||
console.log(` ${component.padEnd(20)} ${count.toString().padStart(4)} ${bar}`);
|
||||
});
|
||||
console.log('');
|
||||
|
||||
// Custom classes analysis
|
||||
const customPatterns = [
|
||||
{ name: 'Dev Tools', pattern: /\.(rpc-tool|websocket|code-tab)/g },
|
||||
{ name: 'Navigation', pattern: /\.(top-nav|side-nav|breadcrumb)/g },
|
||||
{ name: 'Content', pattern: /\.(content-|landing-|page-)/g },
|
||||
{ name: 'Cards', pattern: /\.(card-deck|project-card)/g },
|
||||
{ name: 'Video', pattern: /\.video-/g },
|
||||
{ name: 'Blog', pattern: /\.blog-/g },
|
||||
];
|
||||
|
||||
console.log('🎯 Custom Component Patterns:');
|
||||
console.log('━'.repeat(50));
|
||||
customPatterns.forEach(({ name, pattern }) => {
|
||||
const matches = css.match(pattern);
|
||||
const count = matches ? matches.length : 0;
|
||||
if (count > 0) {
|
||||
console.log(` ${name.padEnd(20)} ${count.toString().padStart(4)}`);
|
||||
}
|
||||
});
|
||||
console.log('');
|
||||
|
||||
// Optimization recommendations
|
||||
console.log('💡 Optimization Recommendations:');
|
||||
console.log('━'.repeat(50));
|
||||
|
||||
const recommendations = [];
|
||||
|
||||
// Check for unused Bootstrap components
|
||||
const lowUsageComponents = componentUsage.filter(c => c.count < 5 && c.count > 0);
|
||||
if (lowUsageComponents.length > 0) {
|
||||
recommendations.push({
|
||||
priority: 'HIGH',
|
||||
message: `${lowUsageComponents.length} Bootstrap components with <5 usages detected`,
|
||||
action: 'Consider manually importing only needed Bootstrap modules'
|
||||
});
|
||||
}
|
||||
|
||||
const noUsageComponents = componentUsage.filter(c => c.count === 0);
|
||||
if (noUsageComponents.length > 0) {
|
||||
recommendations.push({
|
||||
priority: 'HIGH',
|
||||
message: `${noUsageComponents.length} Bootstrap components with 0 usages detected`,
|
||||
action: 'Remove unused components from Bootstrap import'
|
||||
});
|
||||
}
|
||||
|
||||
if (sizeKB > 200) {
|
||||
recommendations.push({
|
||||
priority: 'CRITICAL',
|
||||
message: 'Bundle size exceeds 200KB',
|
||||
action: 'Implement PurgeCSS to remove unused styles'
|
||||
});
|
||||
}
|
||||
|
||||
recommendations.push({
|
||||
priority: 'MEDIUM',
|
||||
message: 'No code splitting detected',
|
||||
action: 'Consider splitting vendor CSS from custom styles'
|
||||
});
|
||||
|
||||
recommendations.forEach(({ priority, message, action }) => {
|
||||
const emoji = priority === 'CRITICAL' ? '🔴' : priority === 'HIGH' ? '🟡' : '🔵';
|
||||
console.log(` ${emoji} [${priority}] ${message}`);
|
||||
console.log(` → ${action}`);
|
||||
console.log('');
|
||||
});
|
||||
|
||||
// Estimate potential savings
|
||||
const estimatedReduction = Math.round(parseFloat(sizeKB) * 0.75);
|
||||
const estimatedFinal = Math.round(parseFloat(sizeKB) * 0.25);
|
||||
|
||||
console.log('📈 Estimated Optimization Potential:');
|
||||
console.log('━'.repeat(50));
|
||||
console.log(` Current Size: ${sizeKB} KB`);
|
||||
console.log(` Potential Savings: ~${estimatedReduction} KB (75%)`);
|
||||
console.log(` Expected Size: ~${estimatedFinal} KB`);
|
||||
console.log('');
|
||||
}
|
||||
|
||||
analyzeCSS();
|
||||
|
||||
652
shared/components/Button/Button.md
Normal file
652
shared/components/Button/Button.md
Normal file
@@ -0,0 +1,652 @@
|
||||
# Button Component Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
The Button component is a scalable, accessible button implementation following the XRPL Brand Design System (BDS). It supports three visual variants (Primary, Secondary, Tertiary) and two color themes (Green, Black), with comprehensive state management and smooth animations.
|
||||
|
||||
## Features
|
||||
|
||||
- **Three Variants**: Primary (solid), Secondary (outline), Tertiary (text-only)
|
||||
- **Two Color Themes**: Green (default) and Black
|
||||
- **Link Support**: Can render as anchor elements for navigation via `href` prop
|
||||
- **Animated Arrow Icon**: Optional icon with smooth hover animations
|
||||
- **Full State Support**: Enabled, Hover, Focus, Active, and Disabled states
|
||||
- **Responsive Design**: Adaptive padding and spacing across breakpoints
|
||||
- **Accessibility**: WCAG compliant with keyboard navigation and screen reader support
|
||||
- **Smooth Animations**: 150ms transitions with custom bezier timing
|
||||
|
||||
## Props API
|
||||
|
||||
```typescript
|
||||
interface ButtonProps {
|
||||
/** Button variant - determines visual style */
|
||||
variant?: 'primary' | 'secondary' | 'tertiary';
|
||||
/** Color theme - green (default) or black */
|
||||
color?: 'green' | 'black';
|
||||
/**
|
||||
* Force the color to remain constant regardless of theme mode.
|
||||
* When true, the button color will not change between light/dark modes.
|
||||
* Use this for buttons on colored backgrounds where black should stay black.
|
||||
*/
|
||||
forceColor?: boolean;
|
||||
/** Button content/label */
|
||||
children: React.ReactNode;
|
||||
/** Click handler */
|
||||
onClick?: () => void;
|
||||
/** Disabled state */
|
||||
disabled?: boolean;
|
||||
/** Button type attribute */
|
||||
type?: 'button' | 'submit' | 'reset';
|
||||
/** Additional CSS classes */
|
||||
className?: string;
|
||||
/** Whether to show the arrow icon */
|
||||
showIcon?: boolean;
|
||||
/** Accessibility label - defaults to button text if not provided */
|
||||
ariaLabel?: string;
|
||||
/** URL to navigate to - renders as a Link instead of button */
|
||||
href?: string;
|
||||
/** Link target - only applies when href is provided */
|
||||
target?: '_self' | '_blank';
|
||||
}
|
||||
```
|
||||
|
||||
### Default Values
|
||||
|
||||
- `variant`: `'primary'`
|
||||
- `color`: `'green'`
|
||||
- `forceColor`: `false`
|
||||
- `disabled`: `false`
|
||||
- `type`: `'button'`
|
||||
- `className`: `''`
|
||||
- `showIcon`: `true`
|
||||
- `ariaLabel`: (derived from children text)
|
||||
- `href`: `undefined`
|
||||
- `target`: `'_self'`
|
||||
|
||||
## Variants
|
||||
|
||||
### Primary Button
|
||||
|
||||
The Primary button is used for the main call-to-action on a page. It features a solid background that fills from bottom-to-top on hover.
|
||||
|
||||
**Visual Characteristics:**
|
||||
- Solid background (Green 300 / Black)
|
||||
- High visual emphasis
|
||||
- Background color transitions on hover
|
||||
- Black text on green background, white text on black background
|
||||
|
||||
**Usage:**
|
||||
```tsx
|
||||
<Button variant="primary" onClick={handleClick}>
|
||||
Get Started
|
||||
</Button>
|
||||
```
|
||||
|
||||
### Secondary Button
|
||||
|
||||
The Secondary button is used for supporting actions. It features an outline style with a transparent background that fills on hover.
|
||||
|
||||
**Visual Characteristics:**
|
||||
- Transparent background with 2px border
|
||||
- Medium visual emphasis
|
||||
- Background fills from bottom-to-top on hover
|
||||
- Green/Black text and border
|
||||
|
||||
**Usage:**
|
||||
```tsx
|
||||
<Button variant="secondary" onClick={handleClick}>
|
||||
Learn More
|
||||
</Button>
|
||||
```
|
||||
|
||||
### Tertiary Button
|
||||
|
||||
The Tertiary button is used for low-emphasis or contextual actions. It appears as text-only with optional underline on hover.
|
||||
|
||||
**Visual Characteristics:**
|
||||
- Text-only, no background or border
|
||||
- Lowest visual emphasis
|
||||
- Underline appears on hover/focus
|
||||
- Different typography (Body R token vs Label R)
|
||||
|
||||
**Usage:**
|
||||
```tsx
|
||||
<Button variant="tertiary" onClick={handleClick}>
|
||||
View Details
|
||||
</Button>
|
||||
```
|
||||
|
||||
## Color Themes
|
||||
|
||||
### Green Theme (Default)
|
||||
|
||||
The green theme uses the XRPL brand green colors:
|
||||
- **Primary**: Green 300 background (#21E46B), Green 200 hover (#70EE97)
|
||||
- **Secondary**: Green 400 text/border (#0DAA3E), Green 500 hover (#078139)
|
||||
- **Tertiary**: Green 400 text (#0DAA3E), Green 500 hover (#078139)
|
||||
|
||||
### Black Theme
|
||||
|
||||
The black theme provides an alternative color scheme:
|
||||
- **Primary**: Black background (#141414), 80% black hover
|
||||
- **Secondary**: Black text/border (#141414), 15% black hover fill
|
||||
- **Tertiary**: Black text (#141414)
|
||||
|
||||
**Usage:**
|
||||
```tsx
|
||||
<Button variant="primary" color="black" onClick={handleClick}>
|
||||
Dark Button
|
||||
</Button>
|
||||
```
|
||||
|
||||
### Force Color (Theme-Independent)
|
||||
|
||||
By default, black buttons automatically switch to green in dark mode for better visibility. However, when placing buttons on colored backgrounds (e.g., lilac, yellow, green), you may want black buttons to remain black regardless of theme mode.
|
||||
|
||||
Use the `forceColor` prop to prevent automatic color switching:
|
||||
|
||||
**Usage:**
|
||||
```tsx
|
||||
{/* Black button that stays black in both light and dark modes */}
|
||||
<Button variant="primary" color="black" forceColor onClick={handleClick}>
|
||||
Always Black
|
||||
</Button>
|
||||
|
||||
{/* Useful for colored backgrounds like in FeatureTwoColumn pattern */}
|
||||
<FeatureTwoColumn color="lilac">
|
||||
<Button variant="primary" color="black" forceColor href="/get-started">
|
||||
Get Started
|
||||
</Button>
|
||||
<Button variant="tertiary" color="black" forceColor href="/learn-more">
|
||||
Learn More
|
||||
</Button>
|
||||
</FeatureTwoColumn>
|
||||
```
|
||||
|
||||
**When to use `forceColor`:**
|
||||
- Buttons on colored backgrounds (lilac, yellow, green variants)
|
||||
- When you need consistent button colors regardless of user's theme preference
|
||||
- Pattern components like `FeatureTwoColumn` where black text is required for readability
|
||||
|
||||
**Note:** The `forceColor` prop only affects the color behavior; all other button functionality (hover animations, focus states, etc.) remains the same.
|
||||
|
||||
## Link Buttons
|
||||
|
||||
The Button component can render as an anchor element for navigation by passing the `href` prop. When `href` is provided, the button is wrapped in a Redocly `Link` component for proper routing support within the application.
|
||||
|
||||
### How It Works
|
||||
|
||||
- When `href` is provided and button is not disabled, renders as `<Link>` (anchor element)
|
||||
- When `href` is not provided or button is disabled, renders as `<button>` element
|
||||
- All visual styles and animations remain identical regardless of element type
|
||||
- The Redocly Link component handles internal routing and external link handling
|
||||
|
||||
### Internal Navigation
|
||||
|
||||
For navigating within the application:
|
||||
|
||||
```tsx
|
||||
<Button variant="primary" href="/docs">
|
||||
View Documentation
|
||||
</Button>
|
||||
|
||||
<Button variant="secondary" href="/about">
|
||||
About Us
|
||||
</Button>
|
||||
```
|
||||
|
||||
### External Links
|
||||
|
||||
For external URLs, use `target="_blank"` to open in a new tab:
|
||||
|
||||
```tsx
|
||||
<Button variant="primary" href="https://xrpl.org" target="_blank">
|
||||
Visit XRPL.org
|
||||
</Button>
|
||||
```
|
||||
|
||||
### Link with Click Handler
|
||||
|
||||
You can combine `href` with `onClick` for tracking or additional logic:
|
||||
|
||||
```tsx
|
||||
<Button
|
||||
variant="primary"
|
||||
href="/signup"
|
||||
onClick={() => trackEvent('signup_click')}
|
||||
>
|
||||
Sign Up
|
||||
</Button>
|
||||
```
|
||||
|
||||
### Disabled Link Buttons
|
||||
|
||||
When `disabled={true}` and `href` is provided, the component renders as a `<button>` element instead of a link to prevent navigation:
|
||||
|
||||
```tsx
|
||||
<Button variant="primary" href="/checkout" disabled>
|
||||
Checkout (Unavailable)
|
||||
</Button>
|
||||
```
|
||||
|
||||
### All Variants as Links
|
||||
|
||||
All button variants support link functionality:
|
||||
|
||||
```tsx
|
||||
{/* Primary link button */}
|
||||
<Button variant="primary" href="/get-started">
|
||||
Get Started
|
||||
</Button>
|
||||
|
||||
{/* Secondary link button */}
|
||||
<Button variant="secondary" href="/learn-more">
|
||||
Learn More
|
||||
</Button>
|
||||
|
||||
{/* Tertiary link button */}
|
||||
<Button variant="tertiary" href="/view-details">
|
||||
View Details
|
||||
</Button>
|
||||
|
||||
{/* Black theme link button */}
|
||||
<Button variant="primary" color="black" href="/dashboard">
|
||||
Dashboard
|
||||
</Button>
|
||||
```
|
||||
|
||||
## States
|
||||
|
||||
### Enabled State
|
||||
|
||||
The default interactive state of the button. All variants display their base styling.
|
||||
|
||||
### Hover State
|
||||
|
||||
Triggered when the user hovers over the button with a mouse:
|
||||
- **Primary/Secondary**: Background fills from bottom-to-top
|
||||
- **Tertiary**: Underline appears, text color darkens
|
||||
- **All Variants**: Arrow icon line shrinks, gap increases (with icon)
|
||||
|
||||
### Focus State
|
||||
|
||||
Triggered when the button receives keyboard focus (Tab key):
|
||||
- Similar visual changes to hover state
|
||||
- Additional focus outline (2px border/outline)
|
||||
- Ensures keyboard accessibility
|
||||
|
||||
### Active State
|
||||
|
||||
Triggered when the button is being pressed:
|
||||
- Returns to default padding/gap
|
||||
- Background resets (for Primary/Secondary)
|
||||
- Maintains visual feedback during press
|
||||
|
||||
### Disabled State
|
||||
|
||||
When `disabled={true}`:
|
||||
- Icon is automatically hidden
|
||||
- Gray text and background (Primary) or border (Secondary)
|
||||
- Cursor changes to `not-allowed`
|
||||
- `pointer-events: none` prevents interaction
|
||||
- `aria-disabled` attribute set for screen readers
|
||||
|
||||
**Usage:**
|
||||
```tsx
|
||||
<Button variant="primary" disabled>
|
||||
Unavailable
|
||||
</Button>
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
### Component Structure
|
||||
|
||||
The Button component uses BEM (Block Element Modifier) naming convention with the `bds` namespace:
|
||||
|
||||
- `.bds-btn` - Base button class
|
||||
- `.bds-btn--primary` - Primary variant modifier
|
||||
- `.bds-btn--secondary` - Secondary variant modifier
|
||||
- `.bds-btn--tertiary` - Tertiary variant modifier
|
||||
- `.bds-btn--green` - Green color theme (default)
|
||||
- `.bds-btn--black` - Black color theme
|
||||
- `.bds-btn--disabled` - Disabled state modifier
|
||||
- `.bds-btn--no-icon` - No icon modifier
|
||||
- `.bds-btn__label` - Label element
|
||||
- `.bds-btn__icon` - Icon container
|
||||
- `.bds-btn__icon-line` - Arrow horizontal line
|
||||
- `.bds-btn__icon-chevron` - Arrow chevron
|
||||
|
||||
### Background Animation
|
||||
|
||||
Primary and Secondary variants use a shared animation pattern:
|
||||
|
||||
1. **Pseudo-element (`::before`)**: Creates the hover background fill
|
||||
2. **Transform Origin**: Set to `bottom center` for bottom-to-top fill
|
||||
3. **Initial State**: `scaleY(0)` - background hidden
|
||||
4. **Hover/Focus**: `scaleY(1)` - background fills from bottom
|
||||
5. **Active**: `scaleY(0)` - background resets during press
|
||||
|
||||
This creates a smooth, directional fill animation that feels natural and responsive.
|
||||
|
||||
### Arrow Icon Animation
|
||||
|
||||
The arrow icon consists of two parts:
|
||||
1. **Horizontal Line**: Shrinks from right to left (`scaleX(0)`) on hover/focus
|
||||
2. **Chevron**: Stays visible, shifts right via increased gap
|
||||
|
||||
The gap between label and icon increases on hover/focus:
|
||||
- **Default**: 16px (desktop), 16px (mobile)
|
||||
- **Hover/Focus**: 22px (desktop), 21px (mobile)
|
||||
|
||||
This creates the illusion of the arrow "moving forward" as the line disappears.
|
||||
|
||||
### Padding Adjustments
|
||||
|
||||
On hover/focus, padding adjusts to accommodate the increased gap:
|
||||
- **Primary**: `8px 19px 8px 20px` → `8px 13px 8px 20px` (desktop)
|
||||
- **Secondary**: `6px 17px 6px 18px` → `6px 11px 6px 18px` (desktop)
|
||||
- **Tertiary**: `8px 20px` → `8px 14px 8px 20px` (desktop)
|
||||
|
||||
These adjustments maintain visual balance while allowing the icon animation to work smoothly.
|
||||
|
||||
### Responsive Behavior
|
||||
|
||||
The component adapts to screen size at the `1023px` breakpoint:
|
||||
|
||||
**Desktop (≥1024px):**
|
||||
- Larger padding values
|
||||
- 22px gap on hover/focus
|
||||
|
||||
**Tablet/Mobile (≤1023px):**
|
||||
- Reduced padding values
|
||||
- 21px gap on hover/focus
|
||||
|
||||
All transitions remain smooth across breakpoints.
|
||||
|
||||
## Typography
|
||||
|
||||
### Primary & Secondary Variants
|
||||
- **Font**: Booton, sans-serif
|
||||
- **Size**: 16px (Label R token)
|
||||
- **Weight**: 400
|
||||
- **Line Height**: 23.2px
|
||||
- **Letter Spacing**: 0px
|
||||
|
||||
### Tertiary Variant
|
||||
- **Font**: Booton, sans-serif
|
||||
- **Size**: 18px (Body R token)
|
||||
- **Weight**: 400
|
||||
- **Line Height**: 26.1px
|
||||
- **Letter Spacing**: -0.5px
|
||||
|
||||
## Spacing & Layout
|
||||
|
||||
- **Border Radius**: 100px (fully rounded)
|
||||
- **Max Height**: 40px
|
||||
- **Icon Size**: 15px × 14px
|
||||
- **Transitions**: 150ms with `cubic-bezier(0.98, 0.12, 0.12, 0.98)`
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```tsx
|
||||
import { Button } from 'shared/components/Button';
|
||||
|
||||
// Primary button (default)
|
||||
<Button onClick={handleClick}>
|
||||
Get Started
|
||||
</Button>
|
||||
|
||||
// Secondary button
|
||||
<Button variant="secondary" onClick={handleClick}>
|
||||
Learn More
|
||||
</Button>
|
||||
|
||||
// Tertiary button
|
||||
<Button variant="tertiary" onClick={handleClick}>
|
||||
View Details
|
||||
</Button>
|
||||
```
|
||||
|
||||
### Form Integration
|
||||
|
||||
```tsx
|
||||
<form onSubmit={handleSubmit}>
|
||||
<Button variant="primary" type="submit">
|
||||
Submit
|
||||
</Button>
|
||||
<Button variant="tertiary" type="reset">
|
||||
Reset
|
||||
</Button>
|
||||
<Button variant="tertiary" type="button" onClick={handleCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
</form>
|
||||
```
|
||||
|
||||
### Without Icon
|
||||
|
||||
```tsx
|
||||
<Button variant="primary" showIcon={false} onClick={handleClick}>
|
||||
No Arrow
|
||||
</Button>
|
||||
```
|
||||
|
||||
### Disabled State
|
||||
|
||||
```tsx
|
||||
<Button variant="primary" disabled>
|
||||
Unavailable
|
||||
</Button>
|
||||
```
|
||||
|
||||
### Color Themes
|
||||
|
||||
```tsx
|
||||
{/* Green theme (default) */}
|
||||
<Button variant="primary" color="green" onClick={handleClick}>
|
||||
Green Button
|
||||
</Button>
|
||||
|
||||
{/* Black theme */}
|
||||
<Button variant="primary" color="black" onClick={handleClick}>
|
||||
Black Button
|
||||
</Button>
|
||||
```
|
||||
|
||||
### Link Buttons
|
||||
|
||||
```tsx
|
||||
{/* Internal navigation */}
|
||||
<Button variant="primary" href="/docs">
|
||||
View Documentation
|
||||
</Button>
|
||||
|
||||
{/* External link (opens in new tab) */}
|
||||
<Button variant="primary" href="https://xrpl.org" target="_blank">
|
||||
Visit XRPL.org
|
||||
</Button>
|
||||
|
||||
{/* Link with click tracking */}
|
||||
<Button
|
||||
variant="secondary"
|
||||
href="/signup"
|
||||
onClick={() => analytics.track('signup_button')}
|
||||
>
|
||||
Sign Up
|
||||
</Button>
|
||||
|
||||
{/* Black theme link button */}
|
||||
<Button variant="primary" color="black" href="/dashboard">
|
||||
Go to Dashboard
|
||||
</Button>
|
||||
```
|
||||
|
||||
### Visual Hierarchy
|
||||
|
||||
```tsx
|
||||
{/* Use variants to create clear visual hierarchy */}
|
||||
<Button variant="primary" onClick={handlePrimaryAction}>
|
||||
Main Action
|
||||
</Button>
|
||||
<Button variant="secondary" onClick={handleSecondaryAction}>
|
||||
Secondary Action
|
||||
</Button>
|
||||
<Button variant="tertiary" onClick={handleTertiaryAction}>
|
||||
Tertiary Action
|
||||
</Button>
|
||||
```
|
||||
|
||||
## Accessibility
|
||||
|
||||
### Keyboard Navigation
|
||||
|
||||
- **Tab**: Focus next button
|
||||
- **Shift+Tab**: Focus previous button
|
||||
- **Enter/Space**: Activate button
|
||||
- **Focus Indicator**: Visible outline/border (2px)
|
||||
- **Disabled buttons**: Not focusable
|
||||
|
||||
### Screen Reader Support
|
||||
|
||||
- Semantic `<button>` element (or `<a>` for link buttons)
|
||||
- Button label announced via `aria-label` attribute
|
||||
- `aria-disabled` attribute for disabled state
|
||||
- Icon marked with `aria-hidden="true"`
|
||||
- Link buttons use proper anchor semantics for navigation
|
||||
|
||||
### Color Contrast
|
||||
|
||||
All variants meet WCAG AA standards:
|
||||
- **Primary**: Black on Green 300 = sufficient contrast
|
||||
- **Secondary/Tertiary**: Green 400/500 on White = 4.52:1 / 5.12:1
|
||||
- **Disabled**: Gray 400/500 indicates non-interactive state
|
||||
|
||||
### Focus Management
|
||||
|
||||
- Focus outline appears on keyboard navigation (`:focus-visible`)
|
||||
- Focus styles match hover styles for consistency
|
||||
- Square corners on Tertiary focus outline for better visibility
|
||||
|
||||
## Design Tokens
|
||||
|
||||
The component uses design tokens from the XRPL Brand Design System:
|
||||
|
||||
### Colors
|
||||
- `$green-100` through `$green-500`
|
||||
- `$gray-200`, `$gray-400`, `$gray-500`
|
||||
- `$white`
|
||||
- Neutral black (`#141414`)
|
||||
|
||||
### Spacing
|
||||
- Border radius: `100px`
|
||||
- Focus border width: `2px`
|
||||
- Responsive breakpoint: `1023px`
|
||||
|
||||
### Motion
|
||||
- Transition duration: `150ms`
|
||||
- Timing function: `cubic-bezier(0.98, 0.12, 0.12, 0.98)`
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use Primary for main actions**: Reserve primary buttons for the most important action on a page
|
||||
2. **Use Secondary for supporting actions**: Use secondary buttons for actions that support the primary action
|
||||
3. **Use Tertiary for low-emphasis actions**: Use tertiary buttons for cancel, skip, or less important actions
|
||||
4. **Maintain visual hierarchy**: Don't use multiple primary buttons on the same page
|
||||
5. **Provide clear labels**: Button text should clearly indicate the action
|
||||
6. **Handle disabled states**: Always provide feedback when actions are unavailable
|
||||
7. **Test keyboard navigation**: Ensure all buttons are accessible via keyboard
|
||||
8. **Consider context**: Choose color theme based on background and design context
|
||||
9. **Use `href` for navigation**: When the button navigates to a new page, use the `href` prop instead of `onClick` with router navigation
|
||||
10. **Use `target="_blank"` for external links**: Always open external URLs in a new tab for better UX
|
||||
11. **Combine `href` with `onClick` for tracking**: When you need both navigation and analytics tracking
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Class Name Generation
|
||||
|
||||
The component builds class names dynamically:
|
||||
|
||||
```typescript
|
||||
const classNames = [
|
||||
'bds-btn',
|
||||
`bds-btn--${variant}`,
|
||||
`bds-btn--${color}`,
|
||||
disabled ? 'bds-btn--disabled' : '',
|
||||
!shouldShowIcon ? 'bds-btn--no-icon' : '',
|
||||
className,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ');
|
||||
```
|
||||
|
||||
### Icon Visibility Logic
|
||||
|
||||
The icon is automatically hidden when:
|
||||
- `showIcon={false}` is passed
|
||||
- `disabled={true}` is set
|
||||
|
||||
This ensures disabled buttons don't show interactive elements.
|
||||
|
||||
### Link Rendering Logic
|
||||
|
||||
The component conditionally renders as different elements:
|
||||
|
||||
```typescript
|
||||
// Render as Link when href is provided and not disabled
|
||||
if (href && !disabled) {
|
||||
return (
|
||||
<Link to={href} target={target} className={classNames}>
|
||||
{content}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
// Otherwise render as button
|
||||
return (
|
||||
<button type={type} className={classNames} disabled={disabled}>
|
||||
{content}
|
||||
</button>
|
||||
);
|
||||
```
|
||||
|
||||
This ensures:
|
||||
- Link buttons use proper anchor semantics for navigation
|
||||
- Disabled state always renders as a button to prevent navigation
|
||||
- Visual styles remain consistent across both element types
|
||||
|
||||
### State Management
|
||||
|
||||
The component manages states through CSS classes and props:
|
||||
- **Disabled**: Controlled via `disabled` prop and `aria-disabled` attribute
|
||||
- **Hover/Focus**: Handled by CSS `:hover` and `:focus-visible` pseudo-classes
|
||||
- **Active**: Handled by CSS `:active` pseudo-class
|
||||
- **Link vs Button**: Determined by presence of `href` prop
|
||||
|
||||
## Browser Support
|
||||
|
||||
The component uses modern CSS features:
|
||||
- CSS Grid/Flexbox (widely supported)
|
||||
- `:focus-visible` (supported in modern browsers)
|
||||
- CSS transforms and transitions (widely supported)
|
||||
- CSS custom properties (supported in modern browsers)
|
||||
|
||||
For older browser support, consider polyfills or fallbacks as needed.
|
||||
|
||||
## Related Components
|
||||
|
||||
- See showcase pages for interactive examples:
|
||||
- `about/button-showcase-tertiary.page.tsx`
|
||||
- Other variant showcase pages
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
shared/components/Button/
|
||||
├── Button.tsx # Component implementation
|
||||
├── Button.scss # Component styles
|
||||
├── Button.md # This documentation
|
||||
└── index.ts # Exports
|
||||
```
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user