Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

# These owners will be the default owners for everything in
# the repo. Unless a later match takes precedence,
* @wackerow @corwintines @pettinarip @minimalsm
* @wackerow @pettinarip @minimalsm

# Owners of specific files
/src/data/consensus-bounty-hunters.json @asanso @fredriksvantes
/src/data/wallets/new-to-crypto.ts @konopkja @minimalsm
/src/data/wallets/new-to-crypto.ts @konopkja @minimalsm
2 changes: 1 addition & 1 deletion .storybook/modes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const viewportModes = breakpointSet.reduce<{
}
}, {})

const localesToTest = ["en", "fa"]
const localesToTest = ["en", "ar"]
const locales = pickBy(baseLocales, (_, key) => localesToTest.includes(key))
export const langModes = Object.keys(locales).reduce<{
[locale: string]: { locale: string }
Expand Down
2 changes: 1 addition & 1 deletion .storybook/next-intl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ export const baseLocales = {
zh: { title: "中国人", left: "Zh" },
ru: { title: "Русский", left: "Ru" },
uk: { title: "українська", left: "Uk" },
fa: { title: "فارسی", left: "Fa" },
ar: { title: "العربية", left: "Ar" },
}

// Only i18n files named in this array are being exposed to Storybook. Add filenames as necessary.
Expand Down
94 changes: 56 additions & 38 deletions app/[locale]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ import type {
} from "@/lib/types"
import { CodeExample } from "@/lib/interfaces"

import ABTestWrapper from "@/components/AB/TestWrapper"
import ActivityStats from "@/components/ActivityStats"
import { ChevronNext } from "@/components/Chevron"
import HomeHero from "@/components/Hero/HomeHero"
import BentoCard from "@/components/Homepage/BentoCard"
import CodeExamples from "@/components/Homepage/CodeExamples"
import HomepageSectionImage from "@/components/Homepage/HomepageSectionImage"
import PersonaModalCTA from "@/components/Homepage/PersonaModalCTA"
import { getBentoBoxItems } from "@/components/Homepage/utils"
import ValuesMarqueeFallback from "@/components/Homepage/ValuesMarquee/Fallback"
import BlockHeap from "@/components/icons/block-heap.svg"
Expand Down Expand Up @@ -450,44 +452,60 @@ const Page = async ({ params }: { params: PageParams }) => {
<MainArticle className="flex w-full flex-col items-center" dir={dir}>
<HomeHero />
<div className="w-full space-y-32 px-4 md:mx-6 lg:space-y-48">
<div className="-mb-8 grid w-full grid-cols-2 gap-x-4 gap-y-8 border-b py-20 md:grid-cols-4 md:gap-x-10 lg:-mb-12">
{subHeroCTAs.map(
({ label, description, href, className, Svg }, idx) => {
const Link = (
props: Omit<
SvgButtonLinkProps,
"Svg" | "href" | "label" | "children"
>
) => (
<SvgButtonLink
Svg={Svg}
href={href}
label={label}
customEventOptions={{
eventCategory,
eventAction: "Top 4 CTAs",
eventName: subHeroCTAs[idx].eventName,
}}
{...props}
>
<p className="text-body">{description}</p>
</SvgButtonLink>
)
return (
<Fragment key={label}>
<Link
className={cn("xl:hidden", className)}
variant="col"
/>
<Link
className={cn("hidden xl:block", className)}
variant="row"
/>
</Fragment>
)
}
)}
</div>
<ABTestWrapper
testKey="HomepagePersonaCTAs"
variants={[
// Original: 4 CTAs grid
<div
key="four-ctas"
className="-mb-8 grid w-full grid-cols-2 gap-x-4 gap-y-8 border-b py-20 md:grid-cols-4 md:gap-x-10 lg:-mb-12"
>
{subHeroCTAs.map(
({ label, description, href, className, Svg }, idx) => {
const Link = (
props: Omit<
SvgButtonLinkProps,
"Svg" | "href" | "label" | "children"
>
) => (
<SvgButtonLink
Svg={Svg}
href={href}
label={label}
customEventOptions={{
eventCategory,
eventAction: "Top 4 CTAs",
eventName: subHeroCTAs[idx].eventName,
}}
{...props}
>
<p className="text-body">{description}</p>
</SvgButtonLink>
)
return (
<Fragment key={label}>
<Link
className={cn("xl:hidden", className)}
variant="col"
/>
<Link
className={cn("hidden xl:block", className)}
variant="row"
/>
</Fragment>
)
}
)}
</div>,
// Variation1: "Start here" button with persona modal
<div
key="persona-modal"
className="flex w-full items-center justify-center border-b pb-10"
>
<PersonaModalCTA eventCategory={eventCategory} />
</div>,
]}
/>

{/* What is Ethereum */}
<Section
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ethereum-org-website",
"version": "10.22.0",
"version": "10.22.1",
"license": "MIT",
"private": true,
"scripts": {
Expand Down
15 changes: 13 additions & 2 deletions src/components/AB/TestDebugPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use client"

import { useRef, useState } from "react"
import { useEffect, useRef, useState } from "react"
import { createPortal } from "react-dom"

import { cn } from "@/lib/utils/cn"

Expand All @@ -19,6 +20,7 @@ export const ABTestDebugPanel = ({
availableVariants,
}: ABTestDebugPanelProps) => {
const [isOpen, setIsOpen] = useState(false)
const [mounted, setMounted] = useState(false)
const [selectedVariant, setSelectedVariant] = useLocalStorage<number | null>(
`ab-test-${testKey}`,
null
Expand All @@ -27,10 +29,14 @@ export const ABTestDebugPanel = ({

useOnClickOutside(panelRef, () => setIsOpen(false))

useEffect(() => {
setMounted(true)
}, [])

const forceVariant = (variantIndex: number) =>
setSelectedVariant(variantIndex)

return (
const panelContent = (
<div
ref={panelRef}
className="fixed bottom-5 right-5 z-modal rounded-lg border-2 bg-background-low p-2.5 font-mono text-xs"
Expand Down Expand Up @@ -69,4 +75,9 @@ export const ABTestDebugPanel = ({
)}
</div>
)

// Only render portal on client side after mount
if (!mounted) return null

return createPortal(panelContent, document.body)
}
46 changes: 39 additions & 7 deletions src/components/Hero/HomeHero/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { getLocale, getTranslations } from "next-intl/server"

import type { ClassNameProp } from "@/lib/types"

import ABTestWrapper from "@/components/AB/TestWrapper"
import LanguageMorpher from "@/components/Homepage/LanguageMorpher"

import { cn } from "@/lib/utils/cn"
Expand Down Expand Up @@ -68,13 +69,44 @@ const HomeHero = async ({
</picture>
</div>
<div className="flex flex-col items-center border-t-[3px] border-primary-low-contrast px-4 py-10 text-center">
<LanguageMorpher />
<div className="flex flex-col items-center gap-y-5 lg:max-w-2xl">
<h1 className="font-black">{t("page-index-title")}</h1>
<p className="max-w-96 text-md text-body-medium lg:text-lg">
{t("page-index-description")}
</p>
</div>
<ABTestWrapper
testKey="HomepagePersonaCTAs"
variants={[
// Original: LanguageMorpher + existing title/description
<div
key="original-hero-content"
className="flex flex-col items-center"
>
<LanguageMorpher />
<div className="flex flex-col items-center gap-y-5 lg:max-w-2xl">
<h1 className="font-black">{t("page-index-title")}</h1>
<p className="max-w-96 text-md text-body-medium lg:text-lg">
{t("page-index-description")}
</p>
</div>
</div>,
// Variation1: New title/subtitle for persona modal
<div
key="persona-hero-content"
className="flex flex-col items-center gap-y-5 lg:max-w-2xl"
>
<LanguageMorpher />
<div className="flex flex-col items-center gap-y-5 lg:max-w-2xl">
<h1 className="font-black">
The internet
<br />
that belongs to you
</h1>
<p className="max-w-lg text-md text-body-medium lg:text-lg">
Create, own, build, connect, and transact.
<br />
Ethereum is a network that everyone can use and anyone can
build on.
</p>
</div>
</div>,
]}
/>
</div>
</div>
)
Expand Down
Loading
Loading