diff --git a/.prettierrc b/.prettierrc
deleted file mode 100644
index dd48c13bb21..00000000000
--- a/.prettierrc
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "endOfLine": "lf",
- "semi": false,
- "singleQuote": false,
- "tabWidth": 2,
- "trailingComma": "es5",
- "plugins": [
- "prettier-plugin-tailwindcss"
- ]
-}
\ No newline at end of file
diff --git a/.yarn/install-state.gz b/.yarn/install-state.gz
new file mode 100644
index 00000000000..63cd39590e2
Binary files /dev/null and b/.yarn/install-state.gz differ
diff --git a/.yarnrc.yml b/.yarnrc.yml
new file mode 100644
index 00000000000..3186f3f0795
--- /dev/null
+++ b/.yarnrc.yml
@@ -0,0 +1 @@
+nodeLinker: node-modules
diff --git a/public/content/developers/docs/nodes-and-clients/client-diversity/index.md b/public/content/developers/docs/nodes-and-clients/client-diversity/index.md
index 4e234ecca24..a7162761972 100644
--- a/public/content/developers/docs/nodes-and-clients/client-diversity/index.md
+++ b/public/content/developers/docs/nodes-and-clients/client-diversity/index.md
@@ -39,20 +39,19 @@ Although these are unlikely scenarios, the Ethereum eco-system can mitigate thei
There is also a human cost to having majority clients. It puts excess strain and responsibility on a small development team. The lesser the client diversity, the greater the burden of responsibility for the developers maintaining the majority client. Spreading this responsibility across multiple teams is good for both the health of Ethereum's network of nodes and its network of people.
-## Current client diversity {#current-client-diversity}
+### Current client diversity {#current-client-diversity}
-
-_Diagram data from [ethernodes.org](https://ethernodes.org) and [clientdiversity.org](https://clientdiversity.org/)_
+
-The two pie charts above show snapshots of the current client diversity for the execution and consensus layers (at time of writing in January 2022). The execution layer is overwhelmingly dominated by [Geth](https://geth.ethereum.org/), with [Open Ethereum](https://openethereum.github.io/) a distant second, [Erigon](https://github.com/ledgerwatch/erigon) third and [Nethermind](https://nethermind.io/) fourth, with other clients comprising less than 1 % of the network. The most commonly used client on the consensus layer - [Prysm](https://prysmaticlabs.com/#projects) - is not as dominant as Geth but still represents over 60% of the network. [Lighthouse](https://lighthouse.sigmaprime.io/) and [Teku](https://consensys.net/knowledge-base/ethereum-2/teku/) make up ~20% and ~14% respectively, and other clients are rarely used.
+The two pie charts above show snapshots of the current client diversity for the execution and consensus layers (at time of writing in June 2025). Client diversity has improved over the years, and the execution layer has seen a reduction in the domination by [Geth](https://geth.ethereum.org/), with [Nethermind](https://www.nethermind.io/nethermind-client) a close second, [Besu](https://besu.hyperledger.org/) third and [Erigon](https://github.com/ledgerwatch/erigon) fourth, with other clients comprising less than 3 % of the network. The most commonly used client on the consensus layer - [Lighthouse](https://lighthouse.sigmaprime.io/) - is quite close with the second most used.[Prysm](https://prysmaticlabs.com/#projects) and [Teku](https://consensys.net/knowledge-base/ethereum-2/teku/) make up ~31% and ~14% respectively, and other clients are rarely used.
-The execution layer data were obtained from [Ethernodes](https://ethernodes.org) on 23-Jan-2022. Data for consensus clients was obtained from [Michael Sproul](https://github.com/sigp/blockprint). Consensus client data is more difficult to obtain because the consensus layer clients do not always have unambiguous traces that can be used to identify them. The data was generated using a classification algorithm that sometimes confuses some of the minority clients (see [here](https://twitter.com/sproulM_/status/1440512518242197516) for more details). In the diagram above, these ambiguous classifications are treated with an either/or label (e.g. Nimbus/Teku). Nevertheless, it is clear that the majority of the network is running Prysm. The data is a snapshot over a fixed set of blocks (in this case Beacon blocks in slots 2048001 to 2164916) and Prysm's dominance has sometimes been higher, exceeding 68%. Despite only being snapshots, the values in the diagram provide a good general sense of the current state of client diversity.
+The execution layer data were obtained from [supermajority.info](https://supermajority.info/) on 21-Jun-2025. Data for consensus clients was obtained from [Michael Sproul](https://github.com/sigp/blockprint). Consensus client data is more difficult to obtain because the consensus layer clients do not always have unambiguous traces that can be used to identify them. The data was generated using a classification algorithm that sometimes confuses some of the minority clients (see [here](https://twitter.com/sproulM_/status/1440512518242197516) for more details). In the diagram above, these ambiguous classifications are treated with an either/or label (e.g. Nimbus/Teku). Nevertheless, it is clear that the majority of the network is running Prysm. Despite only being snapshots, the values in the diagram provide a good general sense of the current state of client diversity.
Up to date client diversity data for the consensus layer is now available at [clientdiversity.org](https://clientdiversity.org/).
## Execution layer {#execution-layer}
-Until now, the conversation around client diversity has focused mainly on the consensus layer. However, the execution client [Geth](https://geth.ethereum.org) currently accounts for around 85% of all nodes. This percentage is problematic for the same reasons as for consensus clients. For example, a bug in Geth affecting transaction handling or constructing execution payloads could lead to consensus clients finalizing problematic or bugged transactions. Therefore, Ethereum would be healthier with a more even distribution of execution clients, ideally with no client representing more than 33% of the network.
+Until now, the conversation around client diversity has focused mainly on the consensus layer. However, the execution client [Geth](https://geth.ethereum.org) currently accounts for around 41% of all nodes. This percentage is problematic for the same reasons as for consensus clients. For example, a bug in Geth affecting transaction handling or constructing execution payloads could lead to consensus clients finalizing problematic or bugged transactions. Therefore, Ethereum would be healthier with a more even distribution of execution clients, ideally with no client representing more than 33% of the network.
## Use a minority client {#use-minority-client}
@@ -66,6 +65,8 @@ Addressing client diversity requires more than individual users to choose minori
[Erigon](https://github.com/ledgerwatch/erigon)
+[Reth](https://github.com/paradigmxyz/reth)
+
[Go-Ethereum](https://geth.ethereum.org/)
### Consensus clients {#consensus-clients}
diff --git a/src/components/MdComponents/index.tsx b/src/components/MdComponents/index.tsx
index 263057ebf58..eb570e29cc2 100644
--- a/src/components/MdComponents/index.tsx
+++ b/src/components/MdComponents/index.tsx
@@ -1,33 +1,34 @@
-import { type ComponentProps, type HTMLAttributes } from "react"
-
-import type { ChildOnlyProp } from "@/lib/types"
-
-import Contributors from "@/components/Contributors"
-import MarkdownImage from "@/components/Image/MarkdownImage"
-import TooltipLink from "@/components/TooltipLink"
-import YouTube from "@/components/YouTube"
-
-import { cn } from "@/lib/utils/cn"
-
-import ContributorsQuizBanner from "../Banners/ContributorsQuizBanner"
-import Card from "../Card"
-import DocLink from "../DocLink"
-import Emoji from "../Emoji"
-import ExpandableCard from "../ExpandableCard"
-import FeaturedText from "../FeaturedText"
-import GlossaryTooltip from "../Glossary/GlossaryTooltip"
-import IdAnchor from "../IdAnchor"
-import InfoBanner from "../InfoBanner"
-import IssuesList from "../IssuesList"
-import LocaleDateTime from "../LocaleDateTime"
-import MainArticle from "../MainArticle"
-import { StandaloneQuizWidget } from "../Quiz/QuizWidget"
-import { ButtonLink } from "../ui/buttons/Button"
-import { Divider } from "../ui/divider"
-import { Flex } from "../ui/flex"
-import { ListItem, OrderedList, UnorderedList } from "../ui/list"
-import { mdxTableComponents } from "../ui/mdx-table-components"
-import { Tag } from "../ui/tag"
+import { type ComponentProps, type HTMLAttributes } from "react";
+
+import type { ChildOnlyProp } from "@/lib/types";
+
+import Contributors from "@/components/Contributors";
+import TooltipLink from "@/components/TooltipLink";
+import YouTube from "@/components/YouTube";
+
+import { cn } from "@/lib/utils/cn";
+
+import ContributorsQuizBanner from "../Banners/ContributorsQuizBanner";
+import Card from "../Card";
+import DocLink from "../DocLink";
+import Emoji from "../Emoji";
+import ExpandableCard from "../ExpandableCard";
+import FeaturedText from "../FeaturedText";
+import GlossaryTooltip from "../Glossary/GlossaryTooltip";
+import IdAnchor from "../IdAnchor";
+import MarkdownImage from "../Image/MarkdownImage";
+import InfoBanner from "../InfoBanner";
+import IssuesList from "../IssuesList";
+import LocaleDateTime from "../LocaleDateTime";
+import MainArticle from "../MainArticle";
+import PieChartContainer from "../PieChart";
+import { StandaloneQuizWidget } from "../Quiz/QuizWidget";
+import { ButtonLink } from "../ui/buttons/Button";
+import { Divider } from "../ui/divider";
+import { Flex } from "../ui/flex";
+import { ListItem, OrderedList, UnorderedList } from "../ui/list";
+import { mdxTableComponents } from "../ui/mdx-table-components";
+import { Tag } from "../ui/tag";
export const commonHeadingAttributes = (className: string, id?: string) => ({
id,
@@ -37,15 +38,15 @@ export const commonHeadingAttributes = (className: string, id?: string) => ({
className
),
"data-group": !!id || undefined,
-})
+});
-type HeadingProps = HTMLAttributes
+type HeadingProps = HTMLAttributes;
export const Heading1 = ({ children, className, ...rest }: HeadingProps) => (
{children}
-)
+);
export const Heading2 = ({
id,
@@ -60,7 +61,7 @@ export const Heading2 = ({
{children}
-)
+);
export const Heading3 = ({
id,
@@ -75,7 +76,7 @@ export const Heading3 = ({
{children}
-)
+);
export const Heading4 = ({
id,
@@ -90,22 +91,22 @@ export const Heading4 = ({
{children}
-)
+);
export const Pre = (props: ChildOnlyProp) => (
-)
+);
export const Paragraph = (props: ChildOnlyProp) => (
-)
+);
export const HR = () => (
-)
+);
// All base html element components
export const htmlElements = {
@@ -123,7 +124,7 @@ export const htmlElements = {
time: LocaleDateTime,
ul: UnorderedList,
...mdxTableComponents,
-}
+};
/**
* Custom React components
@@ -139,17 +140,17 @@ export const Page = ({
)}
{...props}
/>
-)
+);
export const Title = (props: ChildOnlyProp) => (
-)
+);
export const ContentContainer = (props: ComponentProps<"article">) => {
return (
- )
-}
+ );
+};
// All custom React components
export const reactComponents = {
@@ -166,12 +167,13 @@ export const reactComponents = {
GlossaryTooltip,
InfoBanner,
Page,
+ PieChartContainer,
QuizWidget: StandaloneQuizWidget,
IssuesList,
Tag,
Title,
YouTube,
-}
+};
/**
* All base markdown components as default export
@@ -179,6 +181,6 @@ export const reactComponents = {
const MdComponents = {
...htmlElements,
...reactComponents,
-}
+};
-export default MdComponents
+export default MdComponents;
diff --git a/src/components/PieChart/PieChart.tsx b/src/components/PieChart/PieChart.tsx
new file mode 100644
index 00000000000..97ce58a14f6
--- /dev/null
+++ b/src/components/PieChart/PieChart.tsx
@@ -0,0 +1,261 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+"use client";
+import { FaArrowTrendUp } from "react-icons/fa6";
+import {
+ Cell,
+ Legend,
+ Pie,
+ PieChart as RechartsPieChart,
+ ResponsiveContainer,
+} from "recharts";
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card";
+import {
+ ChartConfig,
+ ChartContainer,
+ ChartTooltip,
+ // ChartTooltipContent,
+} from "@/components/ui/chart";
+
+type PieChartDataPoint = { name: string; value: number };
+
+/**
+ * PieChartProps defines the properties for the PieChart component.
+ *
+ * @property {PieChartDataPoint[]} data - The data to be displayed in the chart. Each object should have a `name` and `value` property.
+ * @property {string} [title] - The title of the chart.
+ * @property {string} [description] - The description of the chart.
+ * @property {string} [footerText] - The footer text of the chart.
+ * @property {string} [footerSubText] - The footer subtext of the chart.
+ * @property {boolean} [showPercentage=true] - Whether to show percentage values in legend and tooltips.
+ * @property {number} [minSlicePercentage=1] - Minimum percentage to show individual slices (smaller values grouped as "Other").
+ */
+type PieChartProps = {
+ data: PieChartDataPoint[];
+ title?: string;
+ description?: string;
+ footerText?: string;
+ footerSubText?: string;
+ showPercentage?: boolean;
+ minSlicePercentage?: number;
+};
+
+const defaultChartConfig = {
+ value: {
+ label: "Value",
+ color: "hsl(var(--accent-a))",
+ },
+} satisfies ChartConfig;
+
+const COLORS = [
+ "hsla(var(--accent-a))",
+ "hsla(var(--accent-b))",
+ "hsla(var(--accent-c))",
+ "hsla(var(--accent-a-hover))",
+ "hsla(var(--accent-b-hover))",
+ "hsla(var(--accent-c-hover))",
+];
+
+// Generate HSL colors dynamically if we need more than the predefined set
+const generateColor = (index: number): string => {
+ if (index < COLORS.length) {
+ return COLORS[index];
+ }
+ // Generate colors with good contrast using HSL
+ const hue = (index * 137.508) % 360;
+ const saturation = 65 + (index % 3) * 10;
+ const lightness = 45 + (index % 2) * 10;
+ return `hsl(${hue}, ${saturation}%, ${lightness}%)`;
+};
+
+// Utility function to validate and process data
+const processData = (
+ data: PieChartDataPoint[],
+ minSlicePercentage: number = 1
+): PieChartDataPoint[] => {
+ const nonZeroData = data.filter((item) => item.value > 0);
+
+ const total = nonZeroData.reduce((sum, item) => sum + item.value, 0);
+
+ if (total === 0) return [];
+
+ const mainItems = nonZeroData.filter(
+ (item) => (item.value / total) * 100 >= minSlicePercentage
+ );
+ const smallItems = nonZeroData.filter(
+ (item) => (item.value / total) * 100 < minSlicePercentage
+ );
+
+ // Group small items into "Other" if there are any
+ const processedData = [...mainItems];
+ if (smallItems.length > 0) {
+ const otherValue = smallItems.reduce((sum, item) => sum + item.value, 0);
+ processedData.push({ name: "Other", value: otherValue });
+ }
+
+ return processedData;
+};
+
+/*
+PieChart component renders a pie chart with the provided data, utilizing a comprehensive color palette,
+responsive design, and improved accessibility features.
+*/
+export function PieChart({
+ data,
+ title,
+ description,
+ footerText,
+ footerSubText,
+ showPercentage = true,
+ minSlicePercentage = 0,
+}: PieChartProps) {
+ // Process data to handle edge cases and grouping
+ const processedData = processData(data, minSlicePercentage);
+
+ if (processedData.length === 0) {
+ return (
+
+
+ {title && {title}}
+ {description && {description}}
+
+
+