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
335 changes: 126 additions & 209 deletions app/[locale]/page.tsx

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion redirects.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ module.exports = [
"/guides/how-to-create-an-ethereum-account/",
],
["/deprecated-software", "/apps/"],
["/enterprise/private-ethereum", "/enterprise/"],
["/enterprise", "https://institutions.ethereum.org/"],
["/enterprise/private-ethereum", "https://institutions.ethereum.org/"],
["/dashboards", "/resources"],
["/tds", "/trillion-dollar-security"],
["/10-years", "/10years"],
Expand Down
11 changes: 10 additions & 1 deletion src/components/ActivityStats/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,16 @@ const ActivityStats = async ({ metrics, className }: ActivityStatsProps) => {
"xl:ps-8",
]
return (
<div className={cn("grid w-full grid-cols-1 xl:grid-cols-2", className)}>
<div
className={cn("grid w-full grid-cols-1 xl:grid-cols-2", className)}
itemScope
itemType="https://schema.org/Dataset"
>
<meta itemProp="name" content="Ethereum Network Statistics" />
<meta
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First time seeing these meta item/tags. I assume they are related with the jsonlds. Question: can we declare this in the corresponding page-jsonld files for the home page? these are hardcoded values for a reusable UI component.

itemProp="description"
content="Real-time statistics from the Ethereum network including TVL, staking, and transaction data"
/>
{metrics.map(({ label, apiProvider, apiUrl, state }, idx) => (
<BigNumber
className={gridBorderClasses[idx]}
Expand Down
10 changes: 8 additions & 2 deletions src/components/BigNumber/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,20 @@ const BigNumber = async ({
<div
data-label="big-number"
className={cn(bigNumberVariants({ variant }), className)}
itemScope
itemType="https://schema.org/Observation"
>
{value ? (
<>
<div data-label="value" className={valueVariants({ variant })}>
<div
data-label="value"
className={valueVariants({ variant })}
itemProp="value"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for these meta tags. Can we handle all related jsonld things in the page-jsonld files to keep things separated?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The microdata (itemScope/itemProp) and JSON-LD in page-jsonld files are actually two different approaches to structured data:

  • JSON-LD: A separate <script> block, good for static page-level metadata
  • Microdata: Inline attributes on actual HTML elements, ties structured data directly to visible content

For dynamic values like the stats in ActivityStats and BigNumber, microdata makes more sense because the structured data stays in sync with the rendered values automatically. With JSON-LD, we'd have to duplicate the values or somehow reference the component output, which gets messy.

That said, the hardcoded strings ("Ethereum Network Statistics") in a reusable component is awkward - if these components get used elsewhere with different data, those names would be wrong. Happy to make the schema name/description props instead of hardcoded, or remove the microdata entirely if you'd prefer to keep these components generic?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good.

Yeah, now that I see, the BigNumber is not using any hardcoded value, just declaring the itemProps. Then, the value is dynamic.

The main problem is with the ActivityStatus which has hardcoded values. Couldn't we wrap ActivityStats from the consumer side and leave it untouched? example,

// page level
<div itemScope itemType="https://schema.org/Dataset">
    <meta itemProp="name" content="Ethereum Network Statistics" />
    <meta itemProp="description" content="Ethereum Network Statistics" />
    
    <ActivityStats metrics={metrics} />
</div>

>
{value}
</div>
<div className={childrenVariants({ variant })}>
{children}
<span itemProp="name">{children}</span>
{sourceName && sourceUrl && (
<>
&nbsp;
Expand Down
56 changes: 56 additions & 0 deletions src/components/EthPriceSimple.tsx
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this one-off component abstracted? It's all server-side, would suggest we apply this straight to the app/[locale]/page.tsx

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i defer to @pettinarip on repo code standards. personally i like abstract everything but i don't know ball anymore

Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Info } from "lucide-react"
import { getLocale, getTranslations } from "next-intl/server"

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

import Tooltip from "@/components/Tooltip"
import InlineLink from "@/components/ui/Link"

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

interface EthPriceSimpleProps extends React.HTMLAttributes<HTMLDivElement> {
ethPrice: MetricReturnData
}

const EthPriceSimple = async ({
ethPrice,
className,
...props
}: EthPriceSimpleProps) => {
const locale = await getLocale()
const t = await getTranslations({ locale, namespace: "common" })

const hasError = "error" in ethPrice

const price = hasError
? t("loading-error-refresh")
: formatPriceUSD(ethPrice.value, locale)

const tooltipContent = (
<div>
{t("data-provided-by")}{" "}
<InlineLink href="https://www.coingecko.com/en/coins/ethereum">
coingecko.com
</InlineLink>
</div>
)

return (
<div className={cn("py-4", className)} {...props}>
<div
className={cn("text-5xl font-bold", hasError && "text-md text-error")}
>
{price}
</div>
<div className="mt-1 flex items-center gap-1 text-sm text-body-medium">
{t("eth-current-price")}
<Tooltip content={tooltipContent}>
<Info className="size-4" />
</Tooltip>
</div>
</div>
)
}

export default EthPriceSimple
8 changes: 8 additions & 0 deletions src/components/Homepage/HomepageSectionImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ const imageMap: Record<
desktop: StaticImageData
}
> = {
"what-is-ethereum": {
mobile: learnHubHero,
desktop: learnHubHeroPortrait,
},
"what-is-ether": {
mobile: quizzesHubHero,
desktop: quizzesHubHeroPortrait,
},
activity: {
mobile: layerTwoHubHero,
desktop: layerTwoHubHeroPortrait,
Expand Down
10 changes: 5 additions & 5 deletions src/components/Homepage/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,11 @@ const stylesByPosition: Record<Breakpoint, string[]> = {
cn("lg:col-span-6 lg:row-start-4", flow.lg.down),
],
xl: [
cn("xl:col-span-7 xl:col-start-5 xl:row-start-1", flow.xl.right),
cn("xl:col-span-4 xl:col-start-2 xl:row-start-2", flow.xl.up),
cn("xl:col-span-3 xl:col-start-6 xl:row-start-2", flow.xl.down),
cn("xl:col-span-3 xl:col-start-9 xl:row-span-2 xl:row-start-2", flow.xl.up),
cn("xl:col-span-7 xl:col-start-2 xl:row-start-3", flow.xl.right),
cn("xl:col-span-8 xl:col-start-5 xl:row-start-1", flow.xl.right),
cn("xl:col-span-4 xl:row-start-2", flow.xl.up),
cn("xl:col-span-4 xl:col-start-5 xl:row-start-2", flow.xl.down),
cn("xl:col-span-4 xl:col-start-9 xl:row-span-2 xl:row-start-2", flow.xl.up),
cn("xl:col-span-8 xl:row-start-3", flow.xl.right),
],
}

Expand Down
10 changes: 1 addition & 9 deletions src/components/Nav/Menu/SubMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const SubMenu = ({ lvl, items, activeSection, onClose }: LvlContentProps) => {
<List asChild>
<UnorderedList className="m-0 list-none p-2">
{items.map((item) => {
const { label, icon: Icon, ...action } = item
const { label, ...action } = item
const subItems = action.items || []
const isLink = "href" in action
const isActivePage = isLink && cleanPath(asPath) === action.href
Expand All @@ -86,10 +86,6 @@ const SubMenu = ({ lvl, items, activeSection, onClose }: LvlContentProps) => {
asChild
>
<BaseLink href={action.href!}>
{lvl === 1 && Icon ? (
<Icon className="me-4 h-6 w-6" />
) : null}

<ItemContent item={item} lvl={lvl} />
</BaseLink>
</Button>
Expand All @@ -98,10 +94,6 @@ const SubMenu = ({ lvl, items, activeSection, onClose }: LvlContentProps) => {
<>
<Trigger asChild>
<Button variant="ghost" className={buttonClasses}>
{lvl === 1 && Icon ? (
<Icon className="me-4 h-6 w-6" />
) : null}

<ItemContent item={item} lvl={lvl} />
<ChevronNext />
</Button>
Expand Down
80 changes: 2 additions & 78 deletions src/components/Nav/useNavigation.ts
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine with this. Think there are other, optimized ways, in case we want to keep the icons in the future.

Original file line number Diff line number Diff line change
@@ -1,23 +1,4 @@
import BookIcon from "@/components/icons/book.svg"
import BuildingsIcon from "@/components/icons/buildings.svg"
import CodeSquareIcon from "@/components/icons/code-square.svg"
import CompassIcon from "@/components/icons/compass.svg"
import EthereumIcon from "@/components/icons/ethereum-icon.svg"
import FlagIcon from "@/components/icons/flag.svg"
import Flask from "@/components/icons/flask.svg"
import JournalCodeIcon from "@/components/icons/journal-code.svg"
import LayersIcon from "@/components/icons/layers.svg"
import LightbulbIcon from "@/components/icons/lightbulb.svg"
import MegaphoneIcon from "@/components/icons/megaphone.svg"
import MortarboardIcon from "@/components/icons/mortarboard.svg"
import PinAngleIcon from "@/components/icons/pin-angle.svg"
import SafeIcon from "@/components/icons/safe.svg"
import SignpostIcon from "@/components/icons/signpost.svg"
import SlidersHorizontalCircles from "@/components/icons/sliders-horizontal-circles.svg"
import UiChecksGridIcon from "@/components/icons/ui-checks-grid.svg"
import UsersFourLight from "@/components/icons/users-four-light.svg"

import type { NavItem, NavSections } from "./types"
import type { NavSections } from "./types"

import useTranslation from "@/hooks/useTranslation"
import { buildNavigation } from "@/lib/nav/buildNavigation"
Expand All @@ -27,62 +8,5 @@ export const useNavigation = () => {

const linkSections: NavSections = buildNavigation(t)

const iconById: Record<string, NavItem["icon"]> = {
"learn/overview": CompassIcon,
"learn/basics": UiChecksGridIcon,
"learn/advanced": SlidersHorizontalCircles,
"learn/quizzes": MortarboardIcon,
"use/get-started": PinAngleIcon,
"use/use-cases": LightbulbIcon,
"use/stake": SafeIcon,
"use/networks": LayersIcon,
"build/home": CodeSquareIcon,
"build/get-started": FlagIcon,
"build/docs": JournalCodeIcon,
"build/business": BuildingsIcon,
"participate/community-hub": UsersFourLight,
"participate/events": MegaphoneIcon,
"participate/ethereum-org": EthereumIcon,
"research/whitepaper": BookIcon,
"research/roadmap": SignpostIcon,
"research/research": Flask,
}

const applyIconsToItems = (items: NavItem[]): NavItem[] =>
items.map((item) => {
const icon = item.id ? iconById[item.id] : undefined
if ("items" in item && item.items) {
return {
...item,
...(icon ? { icon } : {}),
items: applyIconsToItems(item.items),
}
}
return { ...item, ...(icon ? { icon } : {}) } as NavItem
})

const linkSectionsWithIcons: NavSections = {
learn: {
...linkSections.learn,
items: applyIconsToItems(linkSections.learn.items),
},
use: {
...linkSections.use,
items: applyIconsToItems(linkSections.use.items),
},
build: {
...linkSections.build,
items: applyIconsToItems(linkSections.build.items),
},
participate: {
...linkSections.participate,
items: applyIconsToItems(linkSections.participate.items),
},
research: {
...linkSections.research,
items: applyIconsToItems(linkSections.research.items),
},
}

return { linkSections: linkSectionsWithIcons }
return { linkSections }
}
48 changes: 30 additions & 18 deletions src/intl/en/page-index.json
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
{
"page-index-activity-description": "Activity from all Ethereum networks",
"page-index-activity-description": "Ethereum is the leading platform for issuing, managing, and settling digital assets. From tokenized money and financial instruments to real-world assets and emerging markets, Ethereum provides a secure, neutral foundation for the digital economy.",
"page-index-activity-subtitle": "Activity on Ethereum Mainnet and Layer 2 networks",
"page-index-activity-tag": "Activity",
"page-index-activity-header": "The strongest ecosystem",
"page-index-activity-action": "More ecosystem resources",
"page-index-activity-action-primary": "Enterprise Ethereum",
"page-index-activity-action-primary": "Ethereum for institutions",
"page-index-bento-header": "A new way to use the internet",
"page-index-bento-assets-action": "More on NFTs",
"page-index-bento-assets-content": "Art, certificates or even real estate can be tokenized. Anything can be a tradable token. Ownership is public and verifiable.",
"page-index-bento-assets-content": "From art to real estate to stocks, any asset can be tokenized on Ethereum to prove and verify ownership digitally. Buy, sell, trade, and create assets and collectibles—anytime, anywhere.",
"page-index-bento-assets-title": "The internet of assets",
"page-index-bento-dapps-action": "Browse apps",
"page-index-bento-dapps-content": "Ethereum apps work without selling your data. Protect your privacy.",
"page-index-bento-dapps-title": "Innovative apps",
"page-index-bento-dapps-content": "Apps built on Ethereum work without selling your data. From social media to gaming to work, use the same account for every innovative app while maintaining privacy and access.",
"page-index-bento-dapps-title": "Apps that respect your privacy",
"page-index-bento-defi-action": "Explore DeFi",
"page-index-bento-defi-content": "Billions can't open bank accounts or freely use their money. Ethereum's financial system is always open and unbiased.",
"page-index-bento-defi-title": "A fairer financial system",
"page-index-bento-networks-action": "Explore benefits",
"page-index-bento-networks-content": "Ethereum is the hub for blockchain innovation. The best projects are built on Ethereum.",
"page-index-bento-defi-content": "Borrow, lend, earn interest, and more, without a bank account. Ethereum's decentralized financial system is open 24/7 to anyone with an internet connection.",
"page-index-bento-defi-title": "A financial system open to all",
"page-index-bento-networks-action": "Discover Layer 2s",
"page-index-bento-networks-content": "Hundreds of Layer 2 networks are built on Ethereum. Enjoy low fees and near-instant transactions while benefiting from Ethereum's proven security.",
"page-index-bento-networks-title": "The network of networks",
"page-index-bento-stablecoins-action": "Discover stablecoins",
"page-index-bento-stablecoins-content": "Stablecoins are currencies that maintain stable value. Their price matches the U.S. dollar or other steady assets.",
"page-index-bento-stablecoins-title": "Crypto without volatility",
"page-index-builders-action-primary": "Builder's Portal",
"page-index-bento-stablecoins-content": "Stablecoins are currencies that maintain a stable price, matched to steady assets like the U.S. dollar. Access global payments instantly or store value in digital dollars on Ethereum.",
"page-index-bento-stablecoins-title": "Digital cash for everyday use",
"page-index-builders-action-primary": "Builder's portal",
"page-index-builders-action-secondary": "Documentation",
"page-index-builders-description": "Ethereum is home to Web3's largest and most vibrant developer ecosystem. Use JavaScript and Python, or learn a smart contract language like Solidity or Vyper to write your own app.",
"page-index-builders-tag": "Builders",
Expand Down Expand Up @@ -53,18 +54,29 @@
"page-index-developers-code-example-title-3": "An open, permissionless DNS",
"page-index-developers-code-examples": "Code examples",
"page-index-events-action": "See all events",
"page-index-events-header": "Events",
"page-index-events-header": "Ethereum events",
"page-index-events-subtitle": "Ethereum communities host events all around the globe, all year long",
"page-index-hero-image-alt": "An illustration of a futuristic city, representing the Ethereum ecosystem.",
"page-index-join-action-contribute-description": "Find out all the different ways you can help ethereum.org grow and be better.",
"page-index-join-action-contribute-label": "How to contribute",
"page-index-join-action-discord-description": "To ask questions, coordinate contribution and join community calls.",
"page-index-join-action-github-description": "Contribute to code, design, articles, etc.",
"page-index-join-action-twitter-description": "To keep up with our updates and important news.",
"page-index-join-description": "This website is open source with hundreds of community contributors. You can propose edits to any of the content on this site.",
"page-index-join-description": "The ethereum.org website is built and maintained by thousands of translators, coders, designers, copywriters, and community members. You can propose edits to any of the content on this open source site.",
"page-index-join-header": "Join ethereum.org",
"page-index-learn-description": "Crypto can feel overwhelming. Don't worry, these materials are designed to help you understand Ethereum in just a few minutes.",
"page-index-join-action-hub": "ethereum.org contributor hub",
"page-index-learn-description": "Ethereum is a decentralized blockchain network and software development platform, powered by the cryptocurrency ether (ETH). These resources are your gateway to confidently navigate, understand, and use Ethereum.",
"page-index-what-is-ethereum-title": "What is Ethereum?",
"page-index-what-is-ethereum-description-1": "Ethereum is a decentralized, open source blockchain network and software development platform, powered by the cryptocurrency ether (ETH). Ethereum is the secure, global foundation for a new generation of unstoppable applications.",
"page-index-what-is-ethereum-description-2": "The Ethereum network is open to everyone: no permission is required. It has no owner, and is built and maintained by thousands of people, organizations, and users around the world.",
"page-index-what-is-ethereum-action": "Learn about Ethereum",
"page-index-what-is-ether-title": "What is ETH?",
"page-index-what-is-ether-description-1": "Ether (ETH) is the native cryptocurrency that powers the Ethereum network, used to pay transaction fees and secure the blockchain through staking.",
"page-index-what-is-ether-description-2": "Beyond its technical role, ETH is open, programmable digital money. It is used for global payments, as collateral for loans, and as a store of value that doesn't rely on any central entity.",
"page-index-what-is-ether-action": "Learn more about ether",
"page-index-learn-tag": "Learn",
"page-index-network-tag": "Network",
"page-index-token-tag": "Token",
"page-index-learn-header": "Understand Ethereum",
"page-index-meta-description": "Ethereum is a global, decentralized platform for money and new kinds of applications. On Ethereum, you can write code that controls money, and build applications accessible anywhere in the world.",
"page-index-meta-title": "Ethereum.org: The complete guide to Ethereum",
Expand All @@ -75,13 +87,13 @@
"page-index-network-stats-total-value-held": "Total value held on Ethereum",
"page-index-popular-topics-ethereum": "What is Ethereum?",
"page-index-popular-topics-header": "Popular topics",
"page-index-popular-topics-action": "Other topics",
"page-index-popular-topics-action": "More guides in Ethereum Learn Hub",
"page-index-popular-topics-roadmap": "Ethereum roadmap",
"page-index-popular-topics-start": "How to start, step by step",
"page-index-popular-topics-start": "Step-by-step Ethereum guides",
"page-index-popular-topics-wallets": "What are crypto wallets?",
"page-index-popular-topics-whitepaper": "Ethereum Whitepaper",
"page-index-posts-action": "Read more on these websites",
"page-index-posts-header": "Recent posts",
"page-index-posts-header": "Ethereum news",
"page-index-posts-subtitle": "The latest blog posts and updates from the community",
"page-index-title": "Welcome to Ethereum",
"page-index-use-cases-tag": "Use cases",
Expand Down
9 changes: 9 additions & 0 deletions src/lib/utils/numbers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,12 @@ export const formatLargeNumber = (value: number, locale: string): string => {
maximumSignificantDigits: 4,
}).format(value)
}

export const formatPriceUSD = (value: number, locale: string): string => {
return new Intl.NumberFormat(locale, {
style: "currency",
currency: "USD",
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}).format(value)
}
2 changes: 1 addition & 1 deletion tailwind.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ const config = {
"4xl": "2rem" /* 32px */,
},
gridTemplateColumns: {
bento: "2rem repeat(10, 1fr) 2rem",
bento: "repeat(12, 1fr)",
},
textUnderlineOffset: {
3: "3px",
Expand Down