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 164e1e648776564c616d5a4b40527c4dcfc9dfc8
Author: Xuanwo <[email protected]>
AuthorDate: Fri May 29 22:36:08 2026 +0800

    feat(website): replace operator verb table with a capabilities section
    
    Swap the abstract Operator verb table for a Capabilities section that
    groups what OpenDAL can do — Read, Write, Manage — drawn from the core
    features: byte-range and concurrent reads, adjacent-range merge, resumable
    reads, multipart and concurrent uploads, append, conditional writes,
    listing, batch delete, server-side copy/rename and presigned URLs.
    
    Also drop the duplicate hero code window and the services caption.
---
 website/src/components/landing/data.js           | 35 ++++++++++
 website/src/components/landing/sections.jsx      | 54 +++++++--------
 website/src/components/landing/styles.module.css | 86 ++++++++++++++----------
 website/src/pages/index.jsx                      |  4 +-
 4 files changed, 112 insertions(+), 67 deletions(-)

diff --git a/website/src/components/landing/data.js 
b/website/src/components/landing/data.js
index dbd7e0588..ac5fa6418 100644
--- a/website/src/components/landing/data.js
+++ b/website/src/components/landing/data.js
@@ -142,6 +142,41 @@ 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",
+    ],
+  },
+  {
+    title: "Write",
+    items: [
+      "Streaming writes of any size",
+      "Multipart upload",
+      "Concurrent part uploads",
+      "Append to existing objects",
+      "Atomic create & overwrite guards",
+    ],
+  },
+  {
+    title: "Manage",
+    items: [
+      "Stat metadata without the body",
+      "Lazy, recursive, paginated listing",
+      "Batch & recursive delete",
+      "Server-side copy & rename",
+      "Presigned URLs for direct access",
+    ],
+  },
+];
+
 // Real adopters from the project's users lists (core + bindings), ordered by
 // GitHub star count (highest first). Logos are the projects' GitHub org
 // avatars, self-hosted under static/img/users/. Refresh with scripts as the
diff --git a/website/src/components/landing/sections.jsx 
b/website/src/components/landing/sections.jsx
index c506c1d49..cfb948b66 100644
--- a/website/src/components/landing/sections.jsx
+++ b/website/src/components/landing/sections.jsx
@@ -29,6 +29,7 @@ import {
   heroStats,
   codeSamples,
   valueProps,
+  capabilityGroups,
   usedBy,
   USERS_LIST_URL,
   serviceGroups,
@@ -38,16 +39,6 @@ import {
   principles,
 } from "./data";
 
-const operatorContract = [
-  {
-    verb: "write · read",
-    text: "Write and read whole objects, or stream byte ranges.",
-  },
-  { verb: "stat", text: "Fetch metadata without transferring the body." },
-  { verb: "list", text: "Walk entries lazily — scoped or fully recursive." },
-  { verb: "delete", text: "Remove single keys or whole batches in one call." },
-];
-
 export function Hero() {
   return (
     <header className={styles.hero}>
@@ -163,28 +154,32 @@ export function ValueProps() {
   );
 }
 
-export function OperatorSection() {
+export function Capabilities() {
   return (
     <section className={`${styles.section} ${styles.sectionSubtle}`}>
       <div className="odl-container">
-        <div className={styles.operatorInner}>
-          <div>
-            <span className="odl-eyebrow">The Operator</span>
-            <h2 className={styles.sectionTitle}>Configure once. Access 
anything.</h2>
-            <p className={styles.sectionLede}>
-              Every backend is reached through one Operator with the same 
verbs.
-              Switch storage or switch language — your logic does not change.
-            </p>
-            <div className={styles.operatorContract}>
-              {operatorContract.map((row) => (
-                <div className={styles.contractRow} key={row.verb}>
-                  <span className={styles.contractVerb}>{row.verb}</span>
-                  <span className={styles.contractText}>{row.text}</span>
-                </div>
-              ))}
+        <div className={styles.sectionHead}>
+          <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.
+          </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>
-          </div>
-          <CodeTabs samples={codeSamples} title="the same contract, every 
language" equalize />
+          ))}
         </div>
       </div>
     </section>
@@ -228,9 +223,6 @@ export function Services() {
           ))}
         </div>
         <div className={styles.servicesFoot}>
-          <span className={styles.servicesMore}>
-            …and many more, from HDFS to Hugging Face.
-          </span>
           <Link
             className={`${styles.btn} ${styles.btnSecondary}`}
             to={DOCS_URL}
diff --git a/website/src/components/landing/styles.module.css 
b/website/src/components/landing/styles.module.css
index d50ef2ee0..19e5f8a3e 100644
--- a/website/src/components/landing/styles.module.css
+++ b/website/src/components/landing/styles.module.css
@@ -400,41 +400,63 @@
   margin: 0;
 }
 
-/* ===== Operator section ================================================= */
-.operatorInner {
+/* ===== Capabilities ===================================================== */
+.capabilityGrid {
   display: grid;
-  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
-  gap: clamp(2rem, 1rem + 4vw, 4rem);
-  align-items: center;
+  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;
 }
-.operatorContract {
-  margin-top: var(--odl-space-6);
-  display: flex;
-  flex-direction: column;
-  gap: var(--odl-space-4);
+.capabilityGroup {
+  background: var(--odl-surface);
+  padding: clamp(1.25rem, 1rem + 1.5vw, 2rem);
 }
-.contractRow {
+.capabilityGroupTitle {
   display: flex;
-  align-items: baseline;
-  gap: var(--odl-space-4);
-  padding-bottom: var(--odl-space-4);
-  border-bottom: 1px solid var(--odl-border);
+  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);
 }
-.contractRow:last-child {
-  border-bottom: 0;
-  padding-bottom: 0;
+.capabilityGroupTitle::before {
+  content: "";
+  width: 0.875rem;
+  height: 0.375rem;
+  background: var(--odl-accent);
+  flex: none;
 }
-.contractVerb {
-  font-family: var(--odl-font-mono);
-  font-size: var(--odl-text-sm);
-  font-weight: 600;
-  color: var(--odl-accent);
-  min-width: 5.5rem;
+.capabilityList {
+  list-style: none;
+  margin: 0;
+  padding: 0;
 }
-.contractText {
+.capabilityItem {
+  display: flex;
+  align-items: baseline;
+  gap: var(--odl-space-3);
+  padding: var(--odl-space-3) 0;
   font-size: var(--odl-text-base);
-  color: var(--odl-fg-muted);
-  line-height: var(--odl-leading-snug);
+  color: var(--odl-fg);
+  border-top: 1px solid var(--odl-border);
+}
+.capabilityItem:first-child {
+  border-top: 0;
+}
+.capabilityItem::before {
+  content: "";
+  width: 0.6rem;
+  height: 2px;
+  margin-top: 0.6em;
+  background: var(--odl-border-strong);
+  flex: none;
 }
 
 /* ===== Services ========================================================= */
@@ -496,14 +518,10 @@
   margin-top: var(--odl-space-6);
   display: flex;
   align-items: center;
-  justify-content: space-between;
+  justify-content: flex-end;
   flex-wrap: wrap;
   gap: var(--odl-space-3);
 }
-.servicesMore {
-  font-size: var(--odl-text-sm);
-  color: var(--odl-fg-muted);
-}
 
 /* ===== Bindings ========================================================= */
 .bindingGrid {
@@ -710,8 +728,8 @@
 /* ===== Responsive ======================================================= */
 @media (max-width: 996px) {
   .heroInner,
-  .operatorInner,
-  .layersInner {
+  .layersInner,
+  .capabilityGrid {
     grid-template-columns: 1fr;
   }
   .serviceGroups {
diff --git a/website/src/pages/index.jsx b/website/src/pages/index.jsx
index d1ff32bde..9d8259343 100644
--- a/website/src/pages/index.jsx
+++ b/website/src/pages/index.jsx
@@ -23,7 +23,7 @@ import {
   Hero,
   UsedBy,
   ValueProps,
-  OperatorSection,
+  Capabilities,
   Services,
   Bindings,
   Layers,
@@ -41,7 +41,7 @@ export default function Home() {
         <Hero />
         <UsedBy />
         <ValueProps />
-        <OperatorSection />
+        <Capabilities />
         <Services />
         <Bindings />
         <Layers />

Reply via email to