From 34828fa6dcc233bb25f6f5119f8fbed11a47a6e6 Mon Sep 17 00:00:00 2001 From: jessiemongeon1 Date: Thu, 18 Jun 2026 11:36:58 -0500 Subject: [PATCH 1/5] docs: redesign docs homepage, search modal, Kapa sidebar, and navbar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Redesign landing page with product-centric cards (Data Storage, Walrus Memory, Walrus Console, Walrus Skills) and integrated search bar - Rewrite search modal to match Sui docs style: inline search input, plain text tabs, Ask Walrus AI banner that opens Kapa with query - Add Kapa sidebar mode (data-view-mode=sidebar) with client module that detects open/close state and shifts doc content to accommodate - Slim down navbar height (4.25rem → 3.25rem), tighten spacing and font sizes for a more compact look Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/site/docusaurus.config.js | 4 + docs/site/src/client/kapa-sidebar.js | 85 +++ docs/site/src/components/LandingPage.tsx | 561 ++++++------------ .../src/components/Search/SearchModal.tsx | 413 +++++++------ docs/site/src/css/custom.css | 73 ++- docs/site/src/theme/Navbar/Content/index.js | 10 +- 6 files changed, 561 insertions(+), 585 deletions(-) create mode 100644 docs/site/src/client/kapa-sidebar.js diff --git a/docs/site/docusaurus.config.js b/docs/site/docusaurus.config.js index 0e20c6f9b5..4c49039be3 100644 --- a/docs/site/docusaurus.config.js +++ b/docs/site/docusaurus.config.js @@ -93,6 +93,7 @@ const config = { clientModules: [ "./src/client/webmcp.js", + "./src/client/kapa-sidebar.js", ], onBrokenLinks: "throw", @@ -297,6 +298,9 @@ const config = { "data-project-name": "Walrus Knowledge", "data-project-color": "#37c3b0ff", "data-button-hide": "true", + "data-view-mode": "sidebar", + "data-modal-overlay-hidden": "true", + "data-modal-lock-scroll": "false", "data-modal-title": "Ask Walrus AI", "data-modal-ask-ai-input-placeholder": "Ask me anything about Walrus!", "data-modal-example-questions": diff --git a/docs/site/src/client/kapa-sidebar.js b/docs/site/src/client/kapa-sidebar.js new file mode 100644 index 0000000000..636d3928b1 --- /dev/null +++ b/docs/site/src/client/kapa-sidebar.js @@ -0,0 +1,85 @@ +// Copyright (c) Walrus Foundation +// SPDX-License-Identifier: Apache-2.0 + +// Detects Kapa sidebar open/close and toggles .kapa-sidebar-open on . +// Kapa renders in Shadow DOM so we can't query its internals. +// Strategy: hook Kapa.open for instant open detection, then use +// elementFromPoint to detect close (checks if right edge of screen +// is covered by a non-docusaurus element). + +if (typeof window !== "undefined") { + const OPEN_CLASS = "kapa-sidebar-open"; + let kapaOpen = false; + let hookedRef = null; + + function syncClass() { + document.documentElement.classList.toggle(OPEN_CLASS, kapaOpen); + } + + function hookKapa() { + if (!window.Kapa || !window.Kapa.open || window.Kapa.open === hookedRef) + return; + + const origOpen = window.Kapa.open; + const origClose = window.Kapa.close; + + window.Kapa.open = function (...args) { + kapaOpen = true; + syncClass(); + return origOpen.apply(this, args); + }; + + window.Kapa.close = function (...args) { + kapaOpen = false; + syncClass(); + return origClose.apply(this, args); + }; + + hookedRef = window.Kapa.open; + } + + // Check if Kapa sidebar is covering the right side of the viewport. + // Since Kapa uses Shadow DOM, we can't query its elements directly. + // Instead, check if the element at the right edge of the screen + // belongs to the doc app or to something else (Kapa's panel). + function isSidebarVisible() { + const x = window.innerWidth - 50; + const y = window.innerHeight / 2; + const el = document.elementFromPoint(x, y); + if (!el) return false; + const docRoot = document.getElementById("__docusaurus"); + if (docRoot && docRoot.contains(el)) return false; + if (el === document.body || el === document.documentElement) return false; + return true; + } + + // Hook into History API so the class persists across SPA navigation. + // Docusaurus uses pushState for client-side routing. + const origPush = history.pushState.bind(history); + const origReplace = history.replaceState.bind(history); + history.pushState = function (...args) { + const r = origPush(...args); + syncClass(); + return r; + }; + history.replaceState = function (...args) { + const r = origReplace(...args); + syncClass(); + return r; + }; + window.addEventListener("popstate", syncClass); + + // Poll every 300ms: re-hook Kapa if it reinitializes, and + // detect open/close state via elementFromPoint fallback. + setInterval(() => { + hookKapa(); + + const visible = isSidebarVisible(); + if (visible && !kapaOpen) { + kapaOpen = true; + } else if (!visible && kapaOpen) { + kapaOpen = false; + } + syncClass(); + }, 300); +} diff --git a/docs/site/src/components/LandingPage.tsx b/docs/site/src/components/LandingPage.tsx index f620b62f37..52d3c23bdd 100644 --- a/docs/site/src/components/LandingPage.tsx +++ b/docs/site/src/components/LandingPage.tsx @@ -1,9 +1,11 @@ // Copyright (c) Walrus Foundation // SPDX-License-Identifier: Apache-2.0 -import React, { useEffect } from 'react'; +import React, { useEffect, useState } from 'react'; +import { createPortal } from 'react-dom'; import Head from '@docusaurus/Head'; import WalrusLogo from '@site/static/img/Walrus_Docs.svg'; +import SearchModal from '@site/src/components/Search/SearchModal'; function hideEl(el: HTMLElement): () => void { const prev = el.style.display; @@ -129,12 +131,12 @@ html, body { background: #0d0f12 !important; } /* ── Hero ── */ .landing-root .hero { - position: relative; padding: 72px 0 56px; overflow: hidden; + position: relative; padding: 80px 0 48px; overflow: hidden; background: var(--black); } .landing-root .hero-inner { position: relative; z-index: 5; max-width: 1500px; - margin-bottom: 28px; padding-top: 20px; + margin-bottom: 36px; padding-top: 20px; } .landing-root .hero-badge { display: inline-block; @@ -155,143 +157,117 @@ html, body { background: #0d0f12 !important; } opacity: 0; animation: landingFadeIn 0.6s ease forwards 0.35s; } -/* ── Quick-start cards ── */ +/* ── Product cards ── */ .landing-root .quickstart { - display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; - padding-bottom: 15px; - opacity: 0; animation: landingFadeIn 0.6s ease forwards 0.5s; -} -@media (max-width: 900px) { - .landing-root .quickstart { grid-template-columns: repeat(2, 1fr); } + display: grid; grid-template-columns: repeat(2, 1fr); gap: 14px; + padding-bottom: 0; + opacity: 0; animation: landingFadeIn 0.6s ease forwards 0.65s; } -@media (max-width: 520px) { +@media (max-width: 640px) { .landing-root .quickstart { grid-template-columns: 1fr; } } .landing-root .qs-card { + position: relative; overflow: hidden; background: var(--surface); border: 1px solid var(--border); - border-radius: var(--radius); padding: 22px 18px; + border-radius: var(--radius); padding: 24px 22px 20px; transition: all 0.25s ease; cursor: pointer; display: flex; flex-direction: column; gap: 8px; } +.landing-root .qs-card::before { + content: ''; position: absolute; inset: 0; opacity: 0; + transition: opacity 0.3s ease; border-radius: inherit; z-index: 0; +} .landing-root .qs-card:hover { - border-color: var(--purple); background: var(--surface-hover); + border-color: rgba(255,255,255,0.18); background: var(--surface-hover); transform: translateY(-2px); - box-shadow: 0 8px 32px rgba(0,0,0,0.3); + box-shadow: 0 10px 32px rgba(0,0,0,0.35); +} +.landing-root .qs-card:hover::before { opacity: 1; } +.landing-root .qs-card--purple::before { + background: radial-gradient(ellipse at top right, rgba(202,177,255,0.07) 0%, transparent 60%); +} +.landing-root .qs-card--mint::before { + background: radial-gradient(ellipse at top right, rgba(152,239,221,0.07) 0%, transparent 60%); +} +.landing-root .qs-card--yellow::before { + background: radial-gradient(ellipse at top right, rgba(232,255,117,0.07) 0%, transparent 60%); +} +.landing-root .qs-card .qs-card-top { + position: relative; z-index: 1; + display: flex; align-items: center; gap: 12px; } .landing-root .qs-card .qs-icon { - width: 34px; height: 34px; border-radius: 9px; - background: var(--purple-dim); + width: 38px; height: 38px; border-radius: 10px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; } -.landing-root .qs-card .qs-icon svg { - width: 17px; height: 17px; color: var(--purple); -} +.landing-root .qs-card--purple .qs-icon { background: rgba(202,177,255,0.12); } +.landing-root .qs-card--mint .qs-icon { background: rgba(152,239,221,0.12); } +.landing-root .qs-card--yellow .qs-icon { background: rgba(232,255,117,0.12); } +.landing-root .qs-card .qs-icon svg { width: 19px; height: 19px; } +.landing-root .qs-card--purple .qs-icon svg { color: var(--purple); } +.landing-root .qs-card--mint .qs-icon svg { color: var(--mint); } +.landing-root .qs-card--yellow .qs-icon svg { color: var(--yellow); } .landing-root .qs-card h3 { - font-size: 0.9rem; font-weight: 600; + position: relative; z-index: 1; + font-size: 1.05rem; font-weight: 600; line-height: 1.3; margin: 0; color: var(--white); } .landing-root .qs-card p { - font-size: 0.8rem; color: var(--white); - line-height: 1.45; flex: 1; margin: 0; + position: relative; z-index: 1; + font-size: 0.85rem; color: var(--white); opacity: 0.5; + line-height: 1.5; margin: 0; } .landing-root .qs-card .qs-arrow { - font-size: 0.75rem; color: var(--purple); font-weight: 500; - display: flex; align-items: center; gap: 4px; margin-top: 2px; -} -.landing-root .qs-card .qs-arrow svg { width: 10px; height: 10px; } - -/* ── Divider ── */ -.landing-root .divider { - border: none; border-top: 1px solid var(--border); margin: 0; -} - -/* ── Section heading ── */ -.landing-root .section-head { - padding: 20px 0 10px; - display: flex; align-items: baseline; gap: 12px; -} -.landing-root .section-head .mono-label { - font-family: var(--mono); font-size: 1.5rem; font-weight: 500; - letter-spacing: 0.1em; text-transform: uppercase; - color: var(--purple); flex-shrink: 0; -} -.landing-root .section-head h2 { - font-size: 1.4rem; font-weight: 500; - letter-spacing: -0.015em; margin: 0; color: var(--white); -} - -/* ── Capabilities ── */ -.landing-root .cap-grid { - display: grid; grid-template-columns: 1fr 1fr; - gap: 10px; padding-bottom: 15px; -} -@media (max-width: 700px) { - .landing-root .cap-grid { grid-template-columns: 1fr; } + position: relative; z-index: 1; + font-size: 0.78rem; font-weight: 500; + display: flex; align-items: center; gap: 5px; margin-top: 4px; + transition: gap 0.2s ease; +} +.landing-root .qs-card--purple .qs-arrow { color: var(--purple); } +.landing-root .qs-card--mint .qs-arrow { color: var(--mint); } +.landing-root .qs-card--yellow .qs-arrow { color: var(--yellow); } +.landing-root .qs-card:hover .qs-arrow { gap: 9px; } +.landing-root .qs-card .qs-arrow svg { width: 11px; height: 11px; } + +/* ── Landing search bar ── */ +.landing-root .landing-search { + max-width: 760px; margin: 0 auto; + padding: 0 0 28px; + opacity: 0; animation: landingFadeIn 0.6s ease forwards 0.5s; } -.landing-root .cap-card { +.landing-root .landing-search-btn { + width: 100%; display: flex; align-items: center; gap: 14px; background: var(--surface); border: 1px solid var(--border); - border-radius: var(--radius); padding: 24px 22px; - transition: border-color 0.2s, background 0.2s; -} -.landing-root .cap-card:hover { - border-color: var(--border-hover); - background: var(--surface-hover); + border-radius: var(--radius); padding: 18px 24px; + cursor: pointer; transition: all 0.2s ease; + font-family: var(--sans); } -.landing-root .cap-card h3 { - font-size: 0.9rem; font-weight: 600; - margin: 0 0 6px 0; color: var(--white); - display: flex; align-items: center; gap: 8px; +.landing-root .landing-search-btn:hover { + border-color: rgba(255,255,255,0.16); background: var(--surface-hover); + box-shadow: 0 6px 24px rgba(0,0,0,0.25); } -.landing-root .cap-card h3 .tag { - font-family: var(--mono); font-size: 0.62rem; font-weight: 500; - color: var(--purple); background: var(--purple-dim); - padding: 2px 7px; border-radius: 4px; letter-spacing: 0.03em; +.landing-root .landing-search-btn svg { + width: 20px; height: 20px; color: var(--purple); flex-shrink: 0; } -.landing-root .cap-card p { - font-size: 0.85rem; color: var(--white); - line-height: 1.55; margin: 0; +.landing-root .landing-search-btn span { + font-size: 1rem; color: rgba(255,255,255,0.35); font-weight: 400; } -.landing-root .cap-card .cap-detail { - margin-top: 10px; padding-top: 10px; - border-top: 1px solid var(--border); - font-family: var(--mono); font-size: 0.72rem; - color: var(--white); line-height: 1.6; opacity: 0.5; +.landing-root .landing-search-btn kbd { + margin-left: auto; font-family: var(--sans); + font-size: 0.72rem; color: rgba(255,255,255,0.25); + background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.08); + border-radius: 6px; padding: 3px 9px; font-weight: 500; } -/* ── Use-case rows ── */ -.landing-root .usecase-grid { - display: grid; grid-template-columns: repeat(4, 1fr); - gap: 10px; padding-bottom: 15px; -} -@media (max-width: 900px) { - .landing-root .usecase-grid { grid-template-columns: repeat(2, 1fr); } -} -@media (max-width: 520px) { - .landing-root .usecase-grid { grid-template-columns: 1fr; } +/* ── Search modal z-index fix ── */ +.landing-root .fixed.inset-0, +.landing-root [class*="z-500"] { + z-index: 9999 !important; } -.landing-root .uc-card { - background: var(--surface); border: 1px solid var(--border); - border-radius: var(--radius); padding: 22px 18px; - transition: border-color 0.2s; -} -.landing-root .uc-card:hover { - border-color: var(--border-hover); -} -.landing-root .uc-card h4 { - font-size: 0.85rem; font-weight: 600; - margin: 0 0 8px 0; color: var(--white); -} -.landing-root .uc-card ul { - list-style: none; padding: 0; margin: 0; -} -.landing-root .uc-card li { - font-size: 0.78rem; color: var(--white); line-height: 1.5; - padding: 2px 0 2px 14px; position: relative; opacity: 0.7; -} -.landing-root .uc-card li::before { - content: ''; position: absolute; left: 0; top: 9px; - width: 4px; height: 4px; border-radius: 50%; - background: var(--purple); opacity: 0.6; + +/* ── Divider ── */ +.landing-root .divider { + border: none; border-top: 1px solid var(--border); margin: 0; } .landing-root .hero-lead { @@ -336,18 +312,9 @@ html, body { background: #0d0f12 !important; } flex-shrink: 0; } -/* ── Not-for ── */ -.landing-root .notfor { padding-bottom: 64px; } -.landing-root .notfor-row { display: flex; gap: 8px; flex-wrap: wrap; } -.landing-root .notfor-chip { - font-size: 0.8rem; color: var(--white); opacity: 0.7; - background: var(--surface); border: 1px solid var(--border); - border-radius: 10px; padding: 9px 16px; -} - /* ── Footer ── */ .landing-root .page-footer { - border-top: 1px solid var(--border); padding: 36px 0; + border-top: 1px solid var(--border); margin-top: 80px; padding: 36px 0; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 16px; @@ -378,6 +345,8 @@ html, body { background: #0d0f12 !important; } `; export default function LandingPage() { + const [searchOpen, setSearchOpen] = useState(false); + useEffect(() => { const cleanups: Array<() => void> = []; @@ -421,9 +390,21 @@ export default function LandingPage() { .querySelectorAll('.landing-root .scroll-reveal') .forEach((el) => obs.observe(el)); + // "/" key opens the search bar + function handleKey(e: KeyboardEvent) { + if (e.key === '/' && !e.metaKey && !e.ctrlKey + && !(e.target instanceof HTMLInputElement) + && !(e.target instanceof HTMLTextAreaElement)) { + e.preventDefault(); + setSearchOpen(true); + } + } + document.addEventListener('keydown', handleKey); + return () => { cleanups.forEach((fn) => fn()); obs.disconnect(); + document.removeEventListener('keydown', handleKey); }; }, []); @@ -508,292 +489,82 @@ export default function LandingPage() { +
+ + {searchOpen && createPortal( + setSearchOpen(false)} />, + document.body, + )} +
+
- -
- - - + +
+
+ + + + + +
+

Data Storage

-

Data Storage

-

- CLI tools, environment setup, and core storage - operations for developers. -

- - Get started {arrowIcon} - +

Verifiable storage, erasure coding, and programmable access.

+ Get started {arrowIcon}
- -
- - - - + +
+
+ + + + +
+

Walrus Memory

-

Walrus Sites

-

- Deploy decentralized static websites with true - decentralization. -

- - Learn more {arrowIcon} - +

Portable, encrypted memory for AI agents.

+ Learn more {arrowIcon}
- -
- - - - + +
+
+ + + + +
+

Walrus Console

-

Service Providers

-

- Operate storage nodes, aggregators, and publishers - on the network. -

- - View guide {arrowIcon} - +

Visual dashboard for the Walrus network.

+ Coming soon {arrowIcon}
- - -
-
- 01 -

Core capabilities

-
-
-
-

Storage & retrieval

-

- Walrus supports writing and reading large blobs of - unstructured data. Data is content-addressed. Any change - to the data produces a new identifier. This makes - integrity tamper-evident and enables independent - verification of stored content. Walrus also enables - anyone to prove that a blob has been stored and remains - available for retrieval. -

-
-
-

Data availability and fault tolerance

-

- Walrus uses erasure coding and high redundancy (~4.5x) - to maintain availability even under partial node - failure. -

-
    -
  • - Reads remain available with up to 2/3 responsive - nodes. -
  • -
  • - Writes tolerate up to 1/3 unavailable nodes. -
  • -
-

- This model is more robust than partial-replication - systems and more cost-efficient than full replication. -

-
-
-

Cost efficiency

-

- Through erasure coding, Walrus maintains storage - overhead at approximately 5x the size of stored data - while delivering strong durability and Byzantine fault - tolerance. This enables production-grade availability - without full replication costs. -

-
-
-

Integration with Sui

-

- Walrus leverages Sui for coordination, attesting - availability, and payments. Storage space is represented - as a resource on Sui, which can be owned, split, merged, - and transferred. Stored blobs are also represented by - objects on Sui, which means that smart contracts can - check whether a blob is available and for how long, - extend its lifetime, or optionally delete it. -

-
-
-

Epochs & WAL

-

- Walrus is operated by a committee of storage nodes that - evolve between epochs. A native token, WAL (and its - subdivision FROST, where 1 WAL is equal to 1 billion - FROST), is used to delegate stake to storage nodes, and - those with high stake become part of the epoch committee. - The WAL token is also used for payments for storage. At - the end of each epoch, rewards for selecting storage - nodes, storing, and serving blobs are distributed to - storage nodes and those that stake with them. All these - processes are mediated by smart contracts on the Sui - platform. -

-
-
-

Flexible access

-

- You can interact with Walrus through a command-line - interface (CLI), software development kits (SDKs), and - Web2 HTTP technologies. Walrus is designed to work well - with traditional caches and content distribution - networks (CDNs), while ensuring all operations can also - be run using local tools to maximize decentralization. -

-
- Interfaces: CLI · SDK · HTTP API -
-
-
- -
-
- 02 -

When to use Walrus

-
-
-
-

Independently verifiable

-

- You need to prove where data came from, confirm it has - not been altered, or anchor workflows to specific - dataset versions. -

-
    -
  • AI model artifacts & agent memory
  • -
  • Execution logs for exchanges
  • -
  • Onchain governance data
  • -
  • Audit trails for financial systems
  • -
-
-
-

Highly available under failure

-

- Your system cannot tolerate downtime, partial node - failure, or data loss. -

-
    -
  • Market infrastructure
  • -
  • Autonomous agents coordinating state
  • -
  • Financial protocols with real risk
  • -
-
-
-

Programmable at the data layer

-

- You need smart contracts to manage, verify, or automate - around stored data. -

-
    -
  • Versioned datasets in AI workflows
  • -
  • Contract-controlled storage lifetimes
  • -
  • Onchain verification of offchain artifacts
  • -
-
-
-

Cost-efficient at scale

-

- You require strong durability and Byzantine fault - tolerance without full-replication overhead. -

-
-
- -
-
- 03 -

When not to use Walrus

-
-

Walrus is not optimized for:

-
    -
  • - Small, ephemeral application state better suited for - direct onchain storage -
  • -
  • Ultra-low-latency in-memory databases
  • -
  • - Pure archival storage without verification requirements -
  • -
-

- Walrus is designed for high-stakes systems where - availability, integrity, and programmability are structural - requirements, not optional features. -

-