You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@opendal.apache.org by xu...@apache.org on 2026/05/29 17:57:07 UTC
(opendal) 04/05: feat(website): add interactive capabilities explorer and used-by wall
This is an automated email from the ASF dual-hosted git repository.
Xuanwo pushed a commit to branch xuanwo/website-redesign
in repository https://gitbox.apache.org/repos/asf/opendal.git
commit 0a9058bee3266d20883927a51e05cad7e398de77
Author: Xuanwo <gi...@xuanwo.io>
AuthorDate: Sat May 30 00:26:30 2026 +0800
feat(website): add interactive capabilities explorer and used-by wall
---
website/src/components/landing/data.js | 130 ++++++++++---
website/src/components/landing/sections.jsx | 125 ++++++++----
website/src/components/landing/styles.module.css | 238 +++++++++++++----------
website/static/img/users/cratedb.png | Bin 0 -> 711 bytes
website/static/img/users/daft.png | Bin 0 -> 6745 bytes
website/static/img/users/databend.png | Bin 13146 -> 3579 bytes
website/static/img/users/datafusion-comet.png | Bin 0 -> 7018 bytes
website/static/img/users/dify.png | Bin 7467 -> 2218 bytes
website/static/img/users/filecodebox.png | Bin 53339 -> 0 bytes
website/static/img/users/gravitino.png | Bin 0 -> 7018 bytes
website/static/img/users/greptimedb.png | Bin 0 -> 6539 bytes
website/static/img/users/hasura.png | Bin 0 -> 5428 bytes
website/static/img/users/iceberg-rust.png | Bin 0 -> 7018 bytes
website/static/img/users/kubeflow-trainer.png | Bin 0 -> 9460 bytes
website/static/img/users/lance.png | Bin 0 -> 3328 bytes
website/static/img/users/llamaindex.png | Bin 0 -> 6890 bytes
website/static/img/users/lnx.png | Bin 0 -> 1895 bytes
website/static/img/users/loco.png | Bin 0 -> 5092 bytes
website/static/img/users/milvus.png | Bin 15620 -> 0 bytes
website/static/img/users/octobase.png | Bin 0 -> 11292 bytes
website/static/img/users/openraft.png | Bin 0 -> 3579 bytes
website/static/img/users/pants.png | Bin 0 -> 2676 bytes
website/static/img/users/pathway.png | Bin 0 -> 20187 bytes
website/static/img/users/questdb.png | Bin 26479 -> 3767 bytes
website/static/img/users/quickwit.png | Bin 0 -> 3667 bytes
website/static/img/users/ragflow.png | Bin 0 -> 9257 bytes
website/static/img/users/risingwave.png | Bin 6252 -> 5562 bytes
website/static/img/users/robustmq.png | Bin 0 -> 1540 bytes
website/static/img/users/rustic.png | Bin 0 -> 12204 bytes
website/static/img/users/sccache.png | Bin 4701 -> 1356 bytes
website/static/img/users/seatunnel.png | Bin 0 -> 7018 bytes
website/static/img/users/slatedb.png | Bin 0 -> 4332 bytes
website/static/img/users/spiceai.png | Bin 0 -> 4518 bytes
website/static/img/users/vaultwarden.png | Bin 0 -> 18431 bytes
website/static/img/users/vector.png | Bin 48196 -> 8211 bytes
website/static/img/users/walrus.png | Bin 0 -> 7880 bytes
website/static/img/users/wrenai.png | Bin 0 -> 2330 bytes
website/static/img/users/zino.png | Bin 0 -> 4517 bytes
38 files changed, 320 insertions(+), 173 deletions(-)
diff --git a/website/src/components/landing/data.js b/website/src/components/landing/data.js
index ac5fa6418..83935b611 100644
--- a/website/src/components/landing/data.js
+++ b/website/src/components/landing/data.js
@@ -142,38 +142,80 @@ export const valueProps = [
},
];
-// What OpenDAL can do, grouped for the capabilities section. Grounded in the
-// core read/write/manage features (ranges, concurrency, multipart, listing …).
-export const capabilityGroups = [
- {
- title: "Read",
- items: [
- "Whole-object or byte-range reads",
- "Concurrent, chunked fetching",
- "Auto-merge nearby ranges",
- "Resume interrupted reads",
- "Conditional & versioned reads",
- ],
+// What OpenDAL can do — grouped for the capabilities explorer. Each item pairs
+// a minimal, real snippet with its docs.rs reference. Grounded in the core
+// read / write / manage APIs (verified against types/operator + types/options).
+const RS = "https://docs.rs/opendal/latest/opendal";
+const opDoc = (method) => `${RS}/struct.Operator.html#method.${method}`;
+
+export const capabilityThemes = [
+ {
+ title: "Read in parallel",
+ blurb: "Fetch a byte range, or a whole object in concurrent chunks.",
+ doc: opDoc("read_with"),
+ code: `let op = Operator::new(S3::default().bucket("data"))?.finish();
+
+// Read just the bytes you need.
+let head = op.read_with("logs/today").range(0..64 * 1024).await?;
+
+// Or pull a large object in parallel chunks.
+let full = op
+ .read_with("big.parquet")
+ .concurrent(8)
+ .chunk(8 * 1024 * 1024)
+ .await?;`,
},
{
- title: "Write",
- items: [
- "Streaming writes of any size",
- "Multipart upload",
- "Concurrent part uploads",
- "Append to existing objects",
- "Atomic create & overwrite guards",
- ],
+ title: "Upload in parts",
+ blurb: "Stream writes of any size as concurrent, multipart uploads.",
+ doc: opDoc("writer_with"),
+ code: `let op = Operator::new(S3::default().bucket("data"))?.finish();
+
+// Open a multipart writer with 8 concurrent parts.
+let mut w = op
+ .writer_with("big.bin")
+ .concurrent(8)
+ .chunk(8 * 1024 * 1024)
+ .await?;
+
+// Stream any number of buffers; close flushes the rest.
+w.write(part_one).await?;
+w.write(part_two).await?;
+w.close().await?;`,
},
{
- title: "Manage",
- items: [
- "Stat metadata without the body",
- "Lazy, recursive, paginated listing",
- "Batch & recursive delete",
- "Server-side copy & rename",
- "Presigned URLs for direct access",
- ],
+ title: "Recover from failure",
+ blurb: "Resume on retry, write atomically, and pin a version.",
+ doc: `${RS}/layers/struct.RetryLayer.html`,
+ code: `// Retries automatically resume interrupted transfers.
+let op = Operator::new(S3::default().bucket("data"))?
+ .layer(RetryLayer::new())
+ .finish();
+
+// Create only if absent.
+op.write_with("once.json", data).if_not_exists(true).await?;
+// Read only if unchanged.
+let doc = op.read_with("doc").if_match(etag).await?;
+// Pin an exact version.
+let pinned = op.read_with("doc").version(version_id).await?;`,
+ },
+ {
+ title: "Work with files",
+ blurb: "Inspect, list, move, and share — without moving bytes.",
+ doc: opDoc("list_with"),
+ code: `// Inspect a file without downloading it.
+let meta = op.stat("report.csv").await?;
+// List a prefix, recursing lazily through the tree.
+let mut entries = op.lister_with("logs/").recursive(true).await?;
+while let Some(entry) = entries.try_next().await? {
+ println!("{}", entry.path());
+}
+// Copy on the server — no download.
+op.copy("draft.md", "final.md").await?;
+// Recursively delete a subtree.
+op.delete_with("tmp/").recursive(true).await?;
+// Presign a temporary, shareable URL.
+let url = op.presign_read("report.csv", Duration::from_secs(3600)).await?;`,
},
];
@@ -181,15 +223,43 @@ export const capabilityGroups = [
// GitHub star count (highest first). Logos are the projects' GitHub org
// avatars, self-hosted under static/img/users/. Refresh with scripts as the
// ecosystem grows. The logo wall shows a responsive subset by viewport width.
+// Public projects with 1,000+ GitHub stars that depend on OpenDAL, sorted by
+// stars. Sourced via crates.io reverse deps + GitHub code search; logos are the
+// owner avatars, mirrored locally under static/img/users to avoid runtime calls.
export const usedBy = [
{ name: "Dify", icon: "/img/users/dify.png", href: "https://github.com/langgenius/dify" },
- { name: "Milvus", icon: "/img/users/milvus.png", href: "https://github.com/milvus-io/milvus" },
+ { name: "RAGFlow", icon: "/img/users/ragflow.png", href: "https://github.com/infiniflow/ragflow" },
+ { name: "Pathway", icon: "/img/users/pathway.png", href: "https://github.com/pathwaycom/pathway" },
+ { name: "Vaultwarden", icon: "/img/users/vaultwarden.png", href: "https://github.com/dani-garcia/vaultwarden" },
+ { name: "LlamaIndex", icon: "/img/users/llamaindex.png", href: "https://github.com/run-llama/llama_index" },
+ { name: "Hasura", icon: "/img/users/hasura.png", href: "https://github.com/hasura/graphql-engine" },
{ name: "Vector", icon: "/img/users/vector.png", href: "https://github.com/vectordotdev/vector" },
{ name: "QuestDB", icon: "/img/users/questdb.png", href: "https://github.com/questdb/questdb" },
+ { name: "WrenAI", icon: "/img/users/wrenai.png", href: "https://github.com/Canner/WrenAI" },
+ { name: "Quickwit", icon: "/img/users/quickwit.png", href: "https://github.com/quickwit-oss/quickwit" },
+ { name: "SeaTunnel", icon: "/img/users/seatunnel.png", href: "https://github.com/apache/seatunnel" },
{ name: "Databend", icon: "/img/users/databend.png", href: "https://github.com/databendlabs/databend" },
{ name: "RisingWave", icon: "/img/users/risingwave.png", href: "https://github.com/risingwavelabs/risingwave" },
- { name: "FileCodeBox", icon: "/img/users/filecodebox.png", href: "https://github.com/vastsa/FileCodeBox" },
+ { name: "Loco", icon: "/img/users/loco.png", href: "https://github.com/loco-rs/loco" },
{ name: "sccache", icon: "/img/users/sccache.png", href: "https://github.com/mozilla/sccache" },
+ { name: "Lance", icon: "/img/users/lance.png", href: "https://github.com/lance-format/lance" },
+ { name: "GreptimeDB", icon: "/img/users/greptimedb.png", href: "https://github.com/GreptimeTeam/greptimedb" },
+ { name: "Daft", icon: "/img/users/daft.png", href: "https://github.com/Eventual-Inc/Daft" },
+ { name: "CrateDB", icon: "/img/users/cratedb.png", href: "https://github.com/crate/crate" },
+ { name: "Pants", icon: "/img/users/pants.png", href: "https://github.com/pantsbuild/pants" },
+ { name: "rustic", icon: "/img/users/rustic.png", href: "https://github.com/rustic-rs/rustic" },
+ { name: "SlateDB", icon: "/img/users/slatedb.png", href: "https://github.com/slatedb/slatedb" },
+ { name: "Gravitino", icon: "/img/users/gravitino.png", href: "https://github.com/apache/gravitino" },
+ { name: "Spice.ai", icon: "/img/users/spiceai.png", href: "https://github.com/spiceai/spiceai" },
+ { name: "Kubeflow Trainer", icon: "/img/users/kubeflow-trainer.png", href: "https://github.com/kubeflow/trainer" },
+ { name: "OctoBase", icon: "/img/users/octobase.png", href: "https://github.com/toeverything/OctoBase" },
+ { name: "Openraft", icon: "/img/users/openraft.png", href: "https://github.com/databendlabs/openraft" },
+ { name: "Walrus", icon: "/img/users/walrus.png", href: "https://github.com/nubskr/walrus" },
+ { name: "RobustMQ", icon: "/img/users/robustmq.png", href: "https://github.com/robustmq/robustmq" },
+ { name: "lnx", icon: "/img/users/lnx.png", href: "https://github.com/lnx-search/lnx" },
+ { name: "Iceberg Rust", icon: "/img/users/iceberg-rust.png", href: "https://github.com/apache/iceberg-rust" },
+ { name: "DataFusion Comet", icon: "/img/users/datafusion-comet.png", href: "https://github.com/apache/datafusion-comet" },
+ { name: "zino", icon: "/img/users/zino.png", href: "https://github.com/zino-rs/zino" },
];
// Where people add their own project (PR to the users list).
diff --git a/website/src/components/landing/sections.jsx b/website/src/components/landing/sections.jsx
index cfb948b66..cfb1d7d16 100644
--- a/website/src/components/landing/sections.jsx
+++ b/website/src/components/landing/sections.jsx
@@ -17,9 +17,10 @@
* under the License.
*/
-import React from "react";
+import React, { useState } from "react";
import Link from "@docusaurus/Link";
import { useBaseUrlUtils } from "@docusaurus/useBaseUrl";
+import CodeBlock from "@theme/CodeBlock";
import CodeTabs from "./CodeTabs";
import styles from "./styles.module.css";
import {
@@ -29,7 +30,7 @@ import {
heroStats,
codeSamples,
valueProps,
- capabilityGroups,
+ capabilityThemes,
usedBy,
USERS_LIST_URL,
serviceGroups,
@@ -93,34 +94,40 @@ export function Hero() {
export function UsedBy() {
const { withBaseUrl } = useBaseUrlUtils();
return (
- <section className={styles.usedBy}>
+ <section className={`${styles.section} ${styles.sectionSubtle}`}>
<div className="odl-container">
- <div className={styles.usedByHead}>
+ <div className={styles.sectionHead}>
<span className="odl-eyebrow">Used by</span>
- <Link className={styles.addLogo} to={USERS_LIST_URL}>
- + add your logo
- </Link>
+ <h2 className={styles.sectionTitle}>
+ Powering AI, analytics, and real-time data
+ </h2>
+ <p className={styles.sectionLede}>
+ OpenDAL runs in production across the open-source ecosystem. These
+ are some of the projects that build on it.
+ </p>
</div>
- <div className={styles.logoWall}>
+ <ul className={styles.logoWall}>
{usedBy.map((u) => (
- <Link
- key={u.name}
- className={styles.logoItem}
- to={u.href}
- title={u.name}
- >
- <img
- className={styles.logoMark}
- src={withBaseUrl(u.icon)}
- alt=""
- width="24"
- height="24"
- loading="lazy"
- />
- <span className={styles.logoName}>{u.name}</span>
- </Link>
+ <li key={u.name}>
+ <Link className={styles.logoItem} to={u.href} title={u.name}>
+ <img
+ className={styles.logoMark}
+ src={withBaseUrl(u.icon)}
+ alt=""
+ width="28"
+ height="28"
+ loading="lazy"
+ />
+ <span className={styles.logoName}>{u.name}</span>
+ </Link>
+ </li>
))}
- </div>
+ <li>
+ <Link className={styles.addLogo} to={USERS_LIST_URL}>
+ + add your logo
+ </Link>
+ </li>
+ </ul>
</div>
</section>
);
@@ -155,6 +162,7 @@ export function ValueProps() {
}
export function Capabilities() {
+ const [active, setActive] = useState(capabilityThemes[0]);
return (
<section className={`${styles.section} ${styles.sectionSubtle}`}>
<div className="odl-container">
@@ -162,24 +170,61 @@ export function Capabilities() {
<span className="odl-eyebrow">Capabilities</span>
<h2 className={styles.sectionTitle}>Configure once. Access anything.</h2>
<p className={styles.sectionLede}>
- One Operator gives you a full toolkit for real-world data —
- streaming, concurrency, multipart uploads, conditionals and
- server-side moves — working the same way on every backend.
+ One Operator is a full toolkit for real-world data — read and write
+ at scale, recover from failures, and work with files — the same way
+ on every backend.
</p>
</div>
- <div className={styles.capabilityGrid}>
- {capabilityGroups.map((group) => (
- <div className={styles.capabilityGroup} key={group.title}>
- <h3 className={styles.capabilityGroupTitle}>{group.title}</h3>
- <ul className={styles.capabilityList}>
- {group.items.map((item) => (
- <li className={styles.capabilityItem} key={item}>
- {item}
- </li>
- ))}
- </ul>
+ <div className={styles.capabilityExplorer}>
+ <ul className={styles.capabilityNav}>
+ {capabilityThemes.map((theme) => {
+ const selected = active.title === theme.title;
+ return (
+ <li key={theme.title}>
+ <Link
+ className={`${styles.capabilityItem} ${
+ selected ? styles.capabilityItemActive : ""
+ }`}
+ to={theme.doc}
+ target="_blank"
+ rel="noreferrer"
+ aria-current={selected ? "true" : undefined}
+ onMouseEnter={() => setActive(theme)}
+ onFocus={() => setActive(theme)}
+ >
+ <span className={styles.capabilityText}>
+ <span className={styles.capabilityItemTitle}>
+ {theme.title}
+ </span>
+ <span className={styles.capabilityItemBlurb}>
+ {theme.blurb}
+ </span>
+ </span>
+ <span className={styles.capabilityArrow} aria-hidden="true">
+ ↗
+ </span>
+ </Link>
+ </li>
+ );
+ })}
+ </ul>
+ <div className={styles.capabilityPreview}>
+ <div className={styles.codeWindow}>
+ <div className={styles.windowBar}>
+ <div className={styles.windowDots} aria-hidden="true">
+ <span />
+ <span />
+ <span />
+ </div>
+ <span className={styles.windowTitle}>{active.title}</span>
+ </div>
+ <div className={`${styles.codeBody} ${styles.capabilityCodeBody}`}>
+ <div className={styles.capabilityCodeFade} key={active.title}>
+ <CodeBlock language="rust">{active.code}</CodeBlock>
+ </div>
+ </div>
</div>
- ))}
+ </div>
</div>
</div>
</section>
diff --git a/website/src/components/landing/styles.module.css b/website/src/components/landing/styles.module.css
index 19e5f8a3e..b76032d14 100644
--- a/website/src/components/landing/styles.module.css
+++ b/website/src/components/landing/styles.module.css
@@ -273,61 +273,42 @@
}
/* ===== Used-by wall ===================================================== */
-.usedBy {
- padding-block: clamp(2.25rem, 1.5rem + 3vw, 3.25rem);
- border-top: 1px solid var(--odl-border);
-}
-/* Top row: "Used by" eyebrow (left) + "+ add your logo" (right) over a rule */
-.usedByHead {
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: var(--odl-space-4);
- padding-bottom: var(--odl-space-4);
- border-bottom: 1px solid var(--odl-border);
-}
-.addLogo {
- font-family: var(--odl-font-mono);
- font-size: var(--odl-text-xs);
- letter-spacing: 0.04em;
- color: var(--odl-fg-subtle);
- text-decoration: none;
- white-space: nowrap;
- transition: color var(--odl-dur) var(--odl-ease);
-}
-.addLogo:hover {
- color: var(--odl-accent);
- text-decoration: none;
-}
-/* Logo wall: grayscale adopter logos that color on hover. Logos are sorted by
- GitHub stars; the count shown adapts to viewport width via the nth-child
- rules below (4 → 6 → 8). */
.logoWall {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(12.5rem, 1fr));
+ gap: var(--odl-space-3);
+}
+.logoWall > li {
display: flex;
- flex-wrap: wrap;
- align-items: center;
- justify-content: space-between;
- gap: var(--odl-space-5) var(--odl-space-6);
- margin-top: var(--odl-space-6);
}
.logoItem {
- display: inline-flex;
+ flex: 1;
+ display: flex;
align-items: center;
gap: var(--odl-space-3);
+ padding: var(--odl-space-4);
+ background: var(--odl-surface);
+ border: 1px solid var(--odl-border);
+ border-radius: var(--odl-radius-md);
text-decoration: none;
filter: grayscale(1);
- opacity: 0.62;
+ opacity: 0.72;
transition: opacity var(--odl-dur) var(--odl-ease),
- filter var(--odl-dur) var(--odl-ease);
+ filter var(--odl-dur) var(--odl-ease),
+ background-color var(--odl-dur) var(--odl-ease);
}
.logoItem:hover {
+ background: var(--odl-surface-2);
filter: grayscale(0);
opacity: 1;
text-decoration: none;
}
.logoMark {
- width: 26px;
- height: 26px;
+ width: 28px;
+ height: 28px;
object-fit: contain;
border-radius: var(--odl-radius-sm);
flex: none;
@@ -338,24 +319,31 @@
font-weight: 500;
color: var(--odl-fg-muted);
white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
}
-
-/* Responsive count: 4 on mobile, 6 on tablet, 8 on desktop. */
-.logoItem:nth-child(n + 5) {
- display: none;
-}
-@media (min-width: 600px) {
- .logoItem:nth-child(n + 5) {
- display: inline-flex;
- }
- .logoItem:nth-child(n + 7) {
- display: none;
- }
+/* "+ add your logo" rendered as the final, dashed tile in the wall. */
+.addLogo {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: var(--odl-space-4);
+ border: 1px dashed var(--odl-border);
+ border-radius: var(--odl-radius-md);
+ font-family: var(--odl-font-mono);
+ font-size: var(--odl-text-xs);
+ letter-spacing: 0.04em;
+ color: var(--odl-fg-subtle);
+ text-decoration: none;
+ white-space: nowrap;
+ transition: color var(--odl-dur) var(--odl-ease),
+ border-color var(--odl-dur) var(--odl-ease);
}
-@media (min-width: 980px) {
- .logoItem:nth-child(n + 7) {
- display: inline-flex;
- }
+.addLogo:hover {
+ color: var(--odl-accent);
+ border-color: var(--odl-accent);
+ text-decoration: none;
}
/* ===== Value props ====================================================== */
@@ -400,63 +388,98 @@
margin: 0;
}
-/* ===== Capabilities ===================================================== */
-.capabilityGrid {
+/* ===== Capabilities (explorer) ========================================== */
+.capabilityExplorer {
display: grid;
- grid-template-columns: repeat(3, 1fr);
- gap: 1px;
- background: var(--odl-border);
- border: 1px solid var(--odl-border);
- border-radius: var(--odl-radius-lg);
- overflow: hidden;
-}
-.capabilityGroup {
- background: var(--odl-surface);
- padding: clamp(1.25rem, 1rem + 1.5vw, 2rem);
-}
-.capabilityGroupTitle {
- display: flex;
- align-items: center;
- gap: var(--odl-space-3);
- font-family: var(--odl-font-mono);
- font-size: var(--odl-text-xs);
- font-weight: 500;
- letter-spacing: var(--odl-tracking-label);
- text-transform: uppercase;
- color: var(--odl-fg-subtle);
- margin: 0 0 var(--odl-space-2);
-}
-.capabilityGroupTitle::before {
- content: "";
- width: 0.875rem;
- height: 0.375rem;
- background: var(--odl-accent);
- flex: none;
+ grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr);
+ gap: clamp(2rem, 1rem + 3vw, 3.5rem);
+ align-items: start;
}
-.capabilityList {
+.capabilityNav {
list-style: none;
margin: 0;
padding: 0;
+ display: flex;
+ flex-direction: column;
+ gap: var(--odl-space-2);
}
.capabilityItem {
display: flex;
- align-items: baseline;
+ align-items: flex-start;
+ justify-content: space-between;
gap: var(--odl-space-3);
- padding: var(--odl-space-3) 0;
- font-size: var(--odl-text-base);
- color: var(--odl-fg);
- border-top: 1px solid var(--odl-border);
+ padding: var(--odl-space-4);
+ border: 1px solid transparent;
+ border-radius: var(--odl-radius-md);
+ text-decoration: none;
+ transition: background-color var(--odl-dur) var(--odl-ease),
+ border-color var(--odl-dur) var(--odl-ease);
}
-.capabilityItem:first-child {
- border-top: 0;
+.capabilityItem:hover {
+ background: var(--odl-surface);
+ text-decoration: none;
}
-.capabilityItem::before {
- content: "";
- width: 0.6rem;
- height: 2px;
- margin-top: 0.6em;
- background: var(--odl-border-strong);
- flex: none;
+.capabilityItemActive {
+ background: var(--odl-surface);
+ border-color: var(--odl-border);
+ box-shadow: var(--odl-shadow-sm);
+}
+.capabilityText {
+ display: flex;
+ flex-direction: column;
+ gap: 3px;
+ min-width: 0;
+}
+.capabilityItemTitle {
+ font-size: var(--odl-text-md);
+ font-weight: 650;
+ letter-spacing: var(--odl-tracking-tight);
+ color: var(--odl-fg-strong);
+}
+.capabilityItemBlurb {
+ font-size: var(--odl-text-sm);
+ line-height: var(--odl-leading-snug);
+ color: var(--odl-fg-muted);
+}
+.capabilityArrow {
+ font-family: var(--odl-font-mono);
+ font-size: var(--odl-text-sm);
+ color: var(--odl-accent);
+ opacity: 0;
+ transition: opacity var(--odl-dur) var(--odl-ease);
+}
+.capabilityItem:hover .capabilityArrow,
+.capabilityItemActive .capabilityArrow {
+ opacity: 1;
+}
+
+/* Sticky code preview that tracks the active capability */
+.capabilityPreview {
+ position: sticky;
+ top: 5rem;
+}
+.capabilityCodeBody {
+ height: 18rem;
+ overflow: auto;
+ background: var(--odl-code-bg);
+}
+/* Fade only the code itself; the panel background stays put, so switching
+ snippets never flashes the window surface behind it. */
+.capabilityCodeFade {
+ animation: odlFade var(--odl-dur) var(--odl-ease);
+}
+@keyframes odlFade {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
+@media (prefers-reduced-motion: reduce) {
+ .capabilityCodeFade {
+ animation: none;
+ }
}
/* ===== Services ========================================================= */
@@ -729,9 +752,17 @@
@media (max-width: 996px) {
.heroInner,
.layersInner,
- .capabilityGrid {
+ .capabilityExplorer {
grid-template-columns: 1fr;
}
+ .capabilityPreview {
+ display: none;
+ }
+ .capabilityNav {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: var(--odl-space-3);
+ }
.serviceGroups {
grid-template-columns: repeat(2, 1fr);
}
@@ -742,7 +773,8 @@
@media (max-width: 640px) {
.valueGrid,
- .serviceGroups {
+ .serviceGroups,
+ .capabilityNav {
grid-template-columns: 1fr;
}
.heroStats {
diff --git a/website/static/img/users/cratedb.png b/website/static/img/users/cratedb.png
new file mode 100644
index 000000000..f65bfd245
Binary files /dev/null and b/website/static/img/users/cratedb.png differ
diff --git a/website/static/img/users/daft.png b/website/static/img/users/daft.png
new file mode 100644
index 000000000..cd845656c
Binary files /dev/null and b/website/static/img/users/daft.png differ
diff --git a/website/static/img/users/databend.png b/website/static/img/users/databend.png
index 27fface5c..908975ca6 100644
Binary files a/website/static/img/users/databend.png and b/website/static/img/users/databend.png differ
diff --git a/website/static/img/users/datafusion-comet.png b/website/static/img/users/datafusion-comet.png
new file mode 100644
index 000000000..c8aaed47d
Binary files /dev/null and b/website/static/img/users/datafusion-comet.png differ
diff --git a/website/static/img/users/dify.png b/website/static/img/users/dify.png
index d08be7198..d4c3195c1 100644
Binary files a/website/static/img/users/dify.png and b/website/static/img/users/dify.png differ
diff --git a/website/static/img/users/filecodebox.png b/website/static/img/users/filecodebox.png
deleted file mode 100644
index ba251b3d8..000000000
Binary files a/website/static/img/users/filecodebox.png and /dev/null differ
diff --git a/website/static/img/users/gravitino.png b/website/static/img/users/gravitino.png
new file mode 100644
index 000000000..c8aaed47d
Binary files /dev/null and b/website/static/img/users/gravitino.png differ
diff --git a/website/static/img/users/greptimedb.png b/website/static/img/users/greptimedb.png
new file mode 100644
index 000000000..de5d5eb61
Binary files /dev/null and b/website/static/img/users/greptimedb.png differ
diff --git a/website/static/img/users/hasura.png b/website/static/img/users/hasura.png
new file mode 100644
index 000000000..bfd043810
Binary files /dev/null and b/website/static/img/users/hasura.png differ
diff --git a/website/static/img/users/iceberg-rust.png b/website/static/img/users/iceberg-rust.png
new file mode 100644
index 000000000..c8aaed47d
Binary files /dev/null and b/website/static/img/users/iceberg-rust.png differ
diff --git a/website/static/img/users/kubeflow-trainer.png b/website/static/img/users/kubeflow-trainer.png
new file mode 100644
index 000000000..8cc9ba859
Binary files /dev/null and b/website/static/img/users/kubeflow-trainer.png differ
diff --git a/website/static/img/users/lance.png b/website/static/img/users/lance.png
new file mode 100644
index 000000000..ed288e5b1
Binary files /dev/null and b/website/static/img/users/lance.png differ
diff --git a/website/static/img/users/llamaindex.png b/website/static/img/users/llamaindex.png
new file mode 100644
index 000000000..731c902e4
Binary files /dev/null and b/website/static/img/users/llamaindex.png differ
diff --git a/website/static/img/users/lnx.png b/website/static/img/users/lnx.png
new file mode 100644
index 000000000..dfae753ef
Binary files /dev/null and b/website/static/img/users/lnx.png differ
diff --git a/website/static/img/users/loco.png b/website/static/img/users/loco.png
new file mode 100644
index 000000000..e952fa04e
Binary files /dev/null and b/website/static/img/users/loco.png differ
diff --git a/website/static/img/users/milvus.png b/website/static/img/users/milvus.png
deleted file mode 100644
index 8e3e38be8..000000000
Binary files a/website/static/img/users/milvus.png and /dev/null differ
diff --git a/website/static/img/users/octobase.png b/website/static/img/users/octobase.png
new file mode 100644
index 000000000..aaf33dfe3
Binary files /dev/null and b/website/static/img/users/octobase.png differ
diff --git a/website/static/img/users/openraft.png b/website/static/img/users/openraft.png
new file mode 100644
index 000000000..908975ca6
Binary files /dev/null and b/website/static/img/users/openraft.png differ
diff --git a/website/static/img/users/pants.png b/website/static/img/users/pants.png
new file mode 100644
index 000000000..b9ebd5fbf
Binary files /dev/null and b/website/static/img/users/pants.png differ
diff --git a/website/static/img/users/pathway.png b/website/static/img/users/pathway.png
new file mode 100644
index 000000000..8939308f2
Binary files /dev/null and b/website/static/img/users/pathway.png differ
diff --git a/website/static/img/users/questdb.png b/website/static/img/users/questdb.png
index 1ae55a66a..663fb3c66 100644
Binary files a/website/static/img/users/questdb.png and b/website/static/img/users/questdb.png differ
diff --git a/website/static/img/users/quickwit.png b/website/static/img/users/quickwit.png
new file mode 100644
index 000000000..230241887
Binary files /dev/null and b/website/static/img/users/quickwit.png differ
diff --git a/website/static/img/users/ragflow.png b/website/static/img/users/ragflow.png
new file mode 100644
index 000000000..5bc5c186f
Binary files /dev/null and b/website/static/img/users/ragflow.png differ
diff --git a/website/static/img/users/risingwave.png b/website/static/img/users/risingwave.png
index aed6574ee..ece3bd7d5 100644
Binary files a/website/static/img/users/risingwave.png and b/website/static/img/users/risingwave.png differ
diff --git a/website/static/img/users/robustmq.png b/website/static/img/users/robustmq.png
new file mode 100644
index 000000000..d13539b64
Binary files /dev/null and b/website/static/img/users/robustmq.png differ
diff --git a/website/static/img/users/rustic.png b/website/static/img/users/rustic.png
new file mode 100644
index 000000000..53e2a2773
Binary files /dev/null and b/website/static/img/users/rustic.png differ
diff --git a/website/static/img/users/sccache.png b/website/static/img/users/sccache.png
index 070e3df52..e5a7b2fc0 100644
Binary files a/website/static/img/users/sccache.png and b/website/static/img/users/sccache.png differ
diff --git a/website/static/img/users/seatunnel.png b/website/static/img/users/seatunnel.png
new file mode 100644
index 000000000..c8aaed47d
Binary files /dev/null and b/website/static/img/users/seatunnel.png differ
diff --git a/website/static/img/users/slatedb.png b/website/static/img/users/slatedb.png
new file mode 100644
index 000000000..16b3d3e9c
Binary files /dev/null and b/website/static/img/users/slatedb.png differ
diff --git a/website/static/img/users/spiceai.png b/website/static/img/users/spiceai.png
new file mode 100644
index 000000000..19e0eceef
Binary files /dev/null and b/website/static/img/users/spiceai.png differ
diff --git a/website/static/img/users/vaultwarden.png b/website/static/img/users/vaultwarden.png
new file mode 100644
index 000000000..394dcae93
Binary files /dev/null and b/website/static/img/users/vaultwarden.png differ
diff --git a/website/static/img/users/vector.png b/website/static/img/users/vector.png
index 8420ae221..b8a7bf4d9 100644
Binary files a/website/static/img/users/vector.png and b/website/static/img/users/vector.png differ
diff --git a/website/static/img/users/walrus.png b/website/static/img/users/walrus.png
new file mode 100644
index 000000000..c87b21729
Binary files /dev/null and b/website/static/img/users/walrus.png differ
diff --git a/website/static/img/users/wrenai.png b/website/static/img/users/wrenai.png
new file mode 100644
index 000000000..5de193a81
Binary files /dev/null and b/website/static/img/users/wrenai.png differ
diff --git a/website/static/img/users/zino.png b/website/static/img/users/zino.png
new file mode 100644
index 000000000..df460333e
Binary files /dev/null and b/website/static/img/users/zino.png differ