This is an automated email from the ASF dual-hosted git repository.

morningman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris-website.git


The following commit(s) were added to refs/heads/master by this push:
     new 95e247b3a87 [refactor](next) add doris mascot (#3656)
95e247b3a87 is described below

commit 95e247b3a870edaa996b78f6447bce8512b4e0f6
Author: Mingyu Chen (Rayner) <[email protected]>
AuthorDate: Sat May 16 00:02:47 2026 -0700

    [refactor](next) add doris mascot (#3656)
---
 src/components/home-next/HomeNext.tsx              |   2 +-
 .../home-next/sections/FeaturesSection.tsx         |   5 +-
 src/components/home-next/sections/HeroSection.scss |  70 ++++++++++++++
 src/components/home-next/sections/HeroSection.tsx  | 101 ++++++++++++++++++++-
 .../home-next/sections/UseCasesSection.scss        |  33 +++----
 .../home-next/sections/UseCasesSection.tsx         |   3 +-
 static/images/next/home-page/doris-mascot.png      | Bin 0 -> 316741 bytes
 7 files changed, 189 insertions(+), 25 deletions(-)

diff --git a/src/components/home-next/HomeNext.tsx 
b/src/components/home-next/HomeNext.tsx
index 50ce993f63a..4270a82bba9 100644
--- a/src/components/home-next/HomeNext.tsx
+++ b/src/components/home-next/HomeNext.tsx
@@ -20,8 +20,8 @@ export default function HomeNext({ onSwitchBack }: 
HomeNextProps): JSX.Element {
             onSwitchBack={onSwitchBack}
         >
             <HeroSection />
-            <FeaturesSection />
             <UseCasesSection />
+            <FeaturesSection />
             <EcosystemSection />
             <DeploymentSection />
             <CommunitySection />
diff --git a/src/components/home-next/sections/FeaturesSection.tsx 
b/src/components/home-next/sections/FeaturesSection.tsx
index 124e99ff257..22ebc0d80e8 100644
--- a/src/components/home-next/sections/FeaturesSection.tsx
+++ b/src/components/home-next/sections/FeaturesSection.tsx
@@ -330,10 +330,7 @@ export function FeaturesSection(): JSX.Element {
                 <div className="features-next__header">
                     <div className="features-next__eyebrow">Core 
Capabilities</div>
                     <h2 className="features-next__headline">
-                        <span className="features-next__headline-line">Build 
For</span>
-                        <span className="features-next__headline-line 
features-next__headline-line--accent">
-                            Every Kind of Modern Analytics Workload
-                        </span>
+                        <span className="features-next__headline-line">Three 
Pillars. One Engine</span>
                     </h2>
                 </div>
                 <div
diff --git a/src/components/home-next/sections/HeroSection.scss 
b/src/components/home-next/sections/HeroSection.scss
index 50d67d855d5..0a779962f2a 100644
--- a/src/components/home-next/sections/HeroSection.scss
+++ b/src/components/home-next/sections/HeroSection.scss
@@ -123,10 +123,12 @@
         display: flex;
         flex-direction: column;
         gap: 2px;
+        position: relative;
     }
 
     &__title-line {
         display: block;
+        position: relative;
     }
 
     &__title-fast {
@@ -140,6 +142,67 @@
         color: var(--hn-yellow);
     }
 
+    // Mascot is anchored inside the "Analytics and" line span:
+    // `bottom: 100%` places the IMG's bottom edge at that line's top, then
+    // `translateY` pushes the IMG back down so the *hand blob* lands on the
+    // letter caps. Arms (thin) start higher and aren't the visual anchor —
+    // the wide round hands are.
+    //
+    // PNG geometry measured from doris-mascot.png (1344x768):
+    //   • thin arms        y=441–510  (above hands, not aligned)
+    //   • hand blob top    y=510      → 33.6% from image bottom
+    //   • hand blob center y≈520      → 32.3% from image bottom
+    //   • hand blob bottom y=548      → 28.6% from image bottom
+    //   • bottom 28.5% (y=548–767)    transparent
+    //
+    // translateY(33%) ≈ "hand top aligned with text line top, hand draping
+    // ~5% of mascot height into the letter caps".
+    &__title-line--mascot-host {
+        position: relative;
+    }
+
+    // Wrapper carries the absolute positioning that used to live on the IMG;
+    // it also acts as the positioning context for the pupil overlays so we
+    // can place them at percentage-of-image coordinates.
+    &__mascot-wrap {
+        display: block;
+        position: absolute;
+        bottom: 100%;
+        // Shift the mascot row left from the line's right edge so all three
+        // characters land over the text. The leftmost (purple) mascot should
+        // line up roughly with the "C" in "ANALYTICS".
+        right: 20%;
+        transform: translateY(34%);
+        width: 45%;
+        max-width: 320px;
+        min-width: 220px;
+        z-index: 2;
+        pointer-events: none;
+        user-select: none;
+        filter: drop-shadow(0 14px 22px rgba(0, 0, 0, 0.28));
+    }
+
+    &__mascot {
+        display: block;
+        width: 100%;
+        height: auto;
+    }
+
+    // Pupils are positioned at eye-white centers via inline `left`/`top` and
+    // `translate(-50%, -50%)`. JS adds an extra `translate(...)` to point
+    // each pupil at the cursor, constrained inside its eye white. Pupil
+    // diameter is 50% of eye-white diameter, leaving 50% of room to move.
+    &__mascot-pupil {
+        position: absolute;
+        width: 1.7%;
+        aspect-ratio: 1;
+        background: var(--hn-ink);
+        border-radius: 50%;
+        transform: translate(-50%, -50%);
+        transition: transform 0.08s ease-out;
+        will-change: transform;
+    }
+
     // ── Subtitle ───────────────────────────────────────────────────────────
 
     &__sub {
@@ -1215,6 +1278,13 @@
         &__title {
             font-size: clamp(38px, 4.8vw, 54px);
         }
+
+        // Mascot only shows on a full-width desktop viewport. Once the
+        // hero starts compressing, the precise right-percentage anchoring
+        // becomes unreliable, so we hide it.
+        &__mascot-wrap {
+            display: none;
+        }
     }
 
     .hn-search-results {
diff --git a/src/components/home-next/sections/HeroSection.tsx 
b/src/components/home-next/sections/HeroSection.tsx
index 0bf955e2433..fe35c3bdb80 100644
--- a/src/components/home-next/sections/HeroSection.tsx
+++ b/src/components/home-next/sections/HeroSection.tsx
@@ -1,4 +1,4 @@
-import React, { JSX, useState, useEffect, useMemo } from 'react';
+import React, { JSX, useState, useEffect, useMemo, useRef } from 'react';
 import './HeroSection.scss';
 
 // ─── SVG atoms 
───────────────────────────────────────────────────────────────
@@ -755,6 +755,102 @@ function ReportCarousel(): JSX.Element {
     );
 }
 
+// ─── Mascot + mouse-tracking eyes 
────────────────────────────────────────────
+
+// Measured from doris-mascot.png (1344x768) — six eye-white centers, sorted
+// left-to-right. Each radius is ~1.7% of image width.
+const MASCOT_EYES: Array<{ cx: number; cy: number }> = [
+    { cx: 22.96, cy: 54.01 },
+    { cx: 28.59, cy: 54.01 },
+    { cx: 51.01, cy: 51.27 },
+    { cx: 56.39, cy: 51.95 },
+    { cx: 74.99, cy: 58.71 },
+    { cx: 80.30, cy: 59.91 },
+];
+const MASCOT_EYE_RADIUS_PCT = 1.7;
+const MASCOT_PUPIL_RADIUS_RATIO = 0.5;
+
+function MascotWithEyes(): JSX.Element {
+    const wrapRef = useRef<HTMLSpanElement>(null);
+    const pupilRefs = useRef<Array<HTMLSpanElement | null>>([]);
+    const lastMouseRef = useRef<{ x: number; y: number } | null>(null);
+
+    useEffect(() => {
+        if (typeof window === 'undefined') return undefined;
+
+        const update = (mouseX: number, mouseY: number) => {
+            const wrap = wrapRef.current;
+            if (!wrap) return;
+            const rect = wrap.getBoundingClientRect();
+            if (rect.width === 0 || rect.height === 0) return;
+            const eyeR = (MASCOT_EYE_RADIUS_PCT / 100) * rect.width;
+            const pupilR = eyeR * MASCOT_PUPIL_RADIUS_RATIO;
+            const maxOffset = eyeR - pupilR;
+
+            MASCOT_EYES.forEach((eye, i) => {
+                const pupil = pupilRefs.current[i];
+                if (!pupil) return;
+                const ex = rect.left + (eye.cx / 100) * rect.width;
+                const ey = rect.top + (eye.cy / 100) * rect.height;
+                const dx = mouseX - ex;
+                const dy = mouseY - ey;
+                const dist = Math.hypot(dx, dy);
+                // Soft follow: pupil ramps to its max offset as the cursor
+                // gets ~80 px away — close cursors don't pin the pupil to
+                // the edge of the eye white.
+                const scale = Math.min(dist / 80, 1);
+                const ox = dist > 0 ? (dx / dist) * maxOffset * scale : 0;
+                const oy = dist > 0 ? (dy / dist) * maxOffset * scale : 0;
+                pupil.style.transform = `translate(calc(-50% + 
${ox.toFixed(2)}px), calc(-50% + ${oy.toFixed(2)}px))`;
+            });
+        };
+
+        const onMouseMove = (e: MouseEvent) => {
+            lastMouseRef.current = { x: e.clientX, y: e.clientY };
+            update(e.clientX, e.clientY);
+        };
+        const onTouchMove = (e: TouchEvent) => {
+            const t = e.touches[0];
+            if (!t) return;
+            lastMouseRef.current = { x: t.clientX, y: t.clientY };
+            update(t.clientX, t.clientY);
+        };
+        const onScroll = () => {
+            const m = lastMouseRef.current;
+            if (m) update(m.x, m.y);
+        };
+
+        window.addEventListener('mousemove', onMouseMove, { passive: true });
+        window.addEventListener('touchmove', onTouchMove, { passive: true });
+        window.addEventListener('scroll', onScroll, { passive: true });
+        return () => {
+            window.removeEventListener('mousemove', onMouseMove);
+            window.removeEventListener('touchmove', onTouchMove);
+            window.removeEventListener('scroll', onScroll);
+        };
+    }, []);
+
+    return (
+        <span className="hero-next__mascot-wrap" ref={wrapRef} 
aria-hidden="true">
+            <img
+                className="hero-next__mascot"
+                src="/images/next/home-page/doris-mascot.png"
+                width={1344}
+                height={768}
+                alt=""
+            />
+            {MASCOT_EYES.map((eye, i) => (
+                <span
+                    key={i}
+                    ref={(el) => { pupilRefs.current[i] = el; }}
+                    className="hero-next__mascot-pupil"
+                    style={{ left: `${eye.cx}%`, top: `${eye.cy}%` }}
+                />
+            ))}
+        </span>
+    );
+}
+
 // ─── HeroSection 
─────────────────────────────────────────────────────────────
 
 export function HeroSection(): JSX.Element {
@@ -785,8 +881,9 @@ export function HeroSection(): JSX.Element {
                                 <span 
className="hero-next__title-accent">Fast</span>
                                 <LightningSvg size={56} />
                             </span>
-                            <span className="hero-next__title-line">
+                            <span className="hero-next__title-line 
hero-next__title-line--mascot-host">
                                 <span 
className="hero-next__title-accent">Analytics</span> and
+                                <MascotWithEyes />
                             </span>
                             <span className="hero-next__title-line">
                                 <span 
className="hero-next__title-accent">Search</span> Database
diff --git a/src/components/home-next/sections/UseCasesSection.scss 
b/src/components/home-next/sections/UseCasesSection.scss
index 780ee084f8a..a50c66d90e1 100644
--- a/src/components/home-next/sections/UseCasesSection.scss
+++ b/src/components/home-next/sections/UseCasesSection.scss
@@ -12,8 +12,8 @@
     --uc-ink: #0F1A14;
 
     position: relative;
-    background: var(--uc-green-dark);
-    color: var(--uc-cream-light);
+    background: var(--uc-cream);
+    color: var(--uc-ink);
     padding: 48px 0 56px;
     overflow: hidden;
     isolation: isolate;
@@ -23,7 +23,7 @@
         position: absolute;
         inset: 0;
         z-index: 0;
-        background-image: radial-gradient(rgba(245, 239, 228, 0.06) 1px, 
transparent 1px);
+        background-image: radial-gradient(rgba(6, 70, 50, 0.07) 1px, 
transparent 1px);
         background-size: 32px 32px;
         mask-image: linear-gradient(180deg, transparent, black 20%, black 80%, 
transparent);
         -webkit-mask-image: linear-gradient(180deg, transparent, black 20%, 
black 80%, transparent);
@@ -42,13 +42,13 @@
 
     &__eyebrow {
         @include type.eyebrow;
-        color: var(--uc-green-glow);
+        color: var(--uc-green-deep);
         margin-bottom: 16px;
     }
 
     &__headline {
         @include type.section-title;
-        color: var(--uc-yellow);
+        color: var(--uc-ink);
         margin: 0;
 
         span {
@@ -57,7 +57,7 @@
     }
 
     &__headline-accent {
-        color: var(--uc-cream-light);
+        color: var(--uc-green-deep);
     }
 
     &__grid {
@@ -72,12 +72,12 @@
         flex-direction: column;
         padding: 20px 22px;
         border-radius: 12px;
-        background: rgba(245, 239, 228, 0.04);
-        border: 1px solid rgba(245, 239, 228, 0.1);
+        background: var(--uc-cream-light);
+        border: 1px solid rgba(6, 70, 50, 0.08);
         color: inherit;
         text-decoration: none;
         overflow: hidden;
-        transition: transform 0.25s ease, background 0.25s ease, border-color 
0.25s ease;
+        transition: transform 0.25s ease, background 0.25s ease, border-color 
0.25s ease, box-shadow 0.25s ease;
 
         &::before {
             content: '';
@@ -86,7 +86,7 @@
             left: 0;
             right: 0;
             height: 2px;
-            background: var(--uc-yellow);
+            background: var(--uc-green-deep);
             transform: translateX(-100%);
             transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
         }
@@ -94,8 +94,9 @@
         &:hover,
         &:focus-visible {
             transform: translateY(-4px);
-            background: rgba(245, 239, 228, 0.07);
-            border-color: rgba(245, 239, 228, 0.22);
+            background: #FFFCF5;
+            border-color: rgba(6, 70, 50, 0.18);
+            box-shadow: 0 12px 24px -16px rgba(6, 70, 50, 0.25);
             color: inherit;
             text-decoration: none;
 
@@ -105,7 +106,7 @@
 
             .use-cases-next__card-link {
                 gap: 10px;
-                color: var(--uc-yellow);
+                color: var(--uc-green-deep);
             }
         }
     }
@@ -117,7 +118,7 @@
         line-height: 1.15;
         letter-spacing: -0.01em;
         text-transform: uppercase;
-        color: var(--uc-cream-light);
+        color: var(--uc-ink);
         margin: 0 0 10px;
     }
 
@@ -125,7 +126,7 @@
         font-family: type.$font-sans;
         font-size: 14px;
         line-height: 1.5;
-        color: rgba(245, 239, 228, 0.78);
+        color: rgba(15, 26, 20, 0.72);
         margin: 0 0 12px;
         flex: 1;
     }
@@ -136,7 +137,7 @@
         gap: 6px;
         margin-top: auto;
         @include type.micro-label;
-        color: rgba(245, 239, 228, 0.7);
+        color: rgba(6, 70, 50, 0.7);
         transition: gap 0.25s ease, color 0.25s ease;
     }
 }
diff --git a/src/components/home-next/sections/UseCasesSection.tsx 
b/src/components/home-next/sections/UseCasesSection.tsx
index 887d6b2e3e7..167c771fcb6 100644
--- a/src/components/home-next/sections/UseCasesSection.tsx
+++ b/src/components/home-next/sections/UseCasesSection.tsx
@@ -46,8 +46,7 @@ export function UseCasesSection(): JSX.Element {
                 <div className="use-cases-next__header">
                     <div className="use-cases-next__eyebrow">Use Cases</div>
                     <h2 className="use-cases-next__headline" 
id="use-cases-next-title">
-                        <span>One Engine,</span>
-                        <span 
className="use-cases-next__headline-accent">Built for the Workloads That 
Matter.</span>
+                        <span>Where Doris Goes To Work</span>
                     </h2>
                 </div>
 
diff --git a/static/images/next/home-page/doris-mascot.png 
b/static/images/next/home-page/doris-mascot.png
new file mode 100644
index 00000000000..2dcb7e29dee
Binary files /dev/null and b/static/images/next/home-page/doris-mascot.png 
differ


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to