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 />
