diff --git a/src/components/ActionCard/ActionCard.stories.tsx b/src/components/ActionCard/ActionCard.stories.tsx new file mode 100644 index 00000000000..caf535d2e73 --- /dev/null +++ b/src/components/ActionCard/ActionCard.stories.tsx @@ -0,0 +1,108 @@ +import { Meta, StoryObj } from "@storybook/nextjs" + +import { ButtonLink } from "@/components/ui/buttons/Button" + +import ActionCard from "." + +import devBlocksImg from "@/public/images/developers-eth-blocks.png" +import enterpriseImg from "@/public/images/enterprise.png" +import communityHeroImg from "@/public/images/heroes/community-hero.png" + +const meta = { + title: "Components / Cards / ActionCard", + component: ActionCard, + parameters: { + chromatic: { disableSnapshot: true }, + docs: { + description: { + component: + "Large dual-pane action card with a tinted image area and content side, used to surface a single high-emphasis link (e.g. community subpage entry points). The whole card is a single link via `LinkBox` / `LinkOverlay`. Use `isRight` and `isBottom` to nudge the image alignment inside its pane.", + }, + }, + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], +} satisfies Meta + +export default meta + +type Story = StoryObj + +export const Default: Story = { + args: { + title: "Get involved", + description: + "The Ethereum community includes people from all backgrounds, working on many different projects.", + href: "/community/", + image: communityHeroImg, + alt: "Community illustration", + }, +} + +export const WithoutDescription: Story = { + args: { + title: "Get involved", + href: "/community/", + image: communityHeroImg, + alt: "Community illustration", + }, +} + +export const WithChildren: Story = { + args: { + title: "Enterprise on Ethereum", + description: + "Public, permissionless infrastructure for building secure, transparent business applications.", + href: "/enterprise/", + image: enterpriseImg, + alt: "Enterprise illustration", + children: ( + + Learn more + + ), + }, +} + +export const ImagePositioning: Story = { + args: { + title: "Image positioning", + description: + "Combine `isRight` and `isBottom` to control where the image sits inside its tinted pane.", + href: "#", + image: devBlocksImg, + alt: "Developer blocks illustration", + isRight: true, + isBottom: false, + }, +} + +export const CommunityPageStyle = { + render: () => ( +
+ + +
+ ), +} diff --git a/src/components/ActionCard.tsx b/src/components/ActionCard/index.tsx similarity index 97% rename from src/components/ActionCard.tsx rename to src/components/ActionCard/index.tsx index 1e178ad9cba..24410f54e31 100644 --- a/src/components/ActionCard.tsx +++ b/src/components/ActionCard/index.tsx @@ -2,12 +2,12 @@ import { StaticImageData } from "next/image" import type { BaseHTMLAttributes, ElementType, ReactNode } from "react" import { Image } from "@/components/Image" +import { Flex } from "@/components/ui/flex" import InlineLink from "@/components/ui/Link" import { LinkBox, LinkOverlay } from "@/components/ui/link-box" import { cn } from "@/lib/utils/cn" -import { Flex } from "./ui/flex" export type ActionCardProps = Omit< BaseHTMLAttributes, "title" diff --git a/src/components/CommentCard/CommentCard.stories.tsx b/src/components/CommentCard/CommentCard.stories.tsx new file mode 100644 index 00000000000..959d93c9ac7 --- /dev/null +++ b/src/components/CommentCard/CommentCard.stories.tsx @@ -0,0 +1,69 @@ +import { Meta, StoryObj } from "@storybook/nextjs" + +import { VStack } from "@/components/ui/flex" + +import CommentCard from "." + +const meta = { + title: "Components / Cards / CommentCard", + component: CommentCard, + parameters: { + chromatic: { disableSnapshot: true }, + docs: { + description: { + component: + "Quote-style attribution card used inline within long-form content to attribute a statement to a named person. The avatar circle shows the first letter of `name` over an accent fill.", + }, + }, + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], +} satisfies Meta + +export default meta + +type Story = StoryObj + +export const Default: Story = { + args: { + description: + "Ethereum is a global, decentralized platform for money and new kinds of applications.", + name: "Tim Beiko", + title: "Protocol Coordination, Ethereum Foundation", + }, +} + +export const LongDescription: Story = { + args: { + description: + "Layer 2 networks settle transactions on Ethereum mainnet while running execution off-chain, which gives users much lower fees and higher throughput without compromising on the security properties of the underlying network. This is the path the ecosystem has converged on for scaling.", + name: "Alex Smirnov", + title: "Co-founder, deBridge", + }, +} + +export const InlineWithProse = { + render: () => ( + +

+ Block proposers and attesters earn rewards for participating honestly in + consensus. The economic incentives have so far been sufficient to keep + the network secure under load. +

+ +

+ Anyone with 32 ETH can run their own validator, and there are also + liquid staking options for smaller stakers. +

+
+ ), +} diff --git a/src/components/GhostCard/GhostCard.stories.tsx b/src/components/GhostCard/GhostCard.stories.tsx new file mode 100644 index 00000000000..dfd03e3e900 --- /dev/null +++ b/src/components/GhostCard/GhostCard.stories.tsx @@ -0,0 +1,78 @@ +import { Meta, StoryObj } from "@storybook/nextjs" + +import Emoji from "@/components/Emoji" +import { VStack } from "@/components/ui/flex" + +import GhostCard from "." + +const meta = { + title: "Components / Cards / GhostCard", + component: GhostCard, + parameters: { + chromatic: { disableSnapshot: true }, + docs: { + description: { + component: + "Card with a subtle offset shadow plate behind it (the 'ghost' layer), used to give a sidebar callout extra visual weight inside long-form content. Accepts arbitrary children -- the wrapper applies the layered look only.", + }, + }, + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], +} satisfies Meta + +export default meta + +type Story = StoryObj + +export const Default: Story = { + args: { + children: ( +

A simple card surface with a layered shadow plate behind it.

+ ), + }, +} + +export const WithEmojiAndHeading: Story = { + args: { + children: ( + <> + +

Bitcoin pizza day

+

+ The first known commercial Bitcoin transaction in 2010 -- two pizzas + for 10,000 BTC -- is a reminder that volatile assets make poor units + of account. Stablecoins exist to fix that. +

+ + ), + }, +} + +export const SideBySide = { + render: () => ( + + + +

For savers

+

+ Hold value without exposure to short-term volatility against the US + dollar. +

+
+ + +

For payments

+

+ Move dollar-denominated value across borders in minutes, settled on + public infrastructure. +

+
+
+ ), +} diff --git a/src/components/GhostCard.tsx b/src/components/GhostCard/index.tsx similarity index 94% rename from src/components/GhostCard.tsx rename to src/components/GhostCard/index.tsx index 68c77866b31..a1c2f9c62f9 100644 --- a/src/components/GhostCard.tsx +++ b/src/components/GhostCard/index.tsx @@ -1,8 +1,8 @@ import React from "react" -import { cn } from "@/lib/utils/cn" +import { Card } from "@/components/ui/card" -import { Card } from "./ui/card" +import { cn } from "@/lib/utils/cn" interface GhostCardProps extends React.HTMLAttributes { children: React.ReactNode diff --git a/src/components/HighlightCard/HighlightCard.stories.tsx b/src/components/HighlightCard/HighlightCard.stories.tsx new file mode 100644 index 00000000000..ed662b86ffb --- /dev/null +++ b/src/components/HighlightCard/HighlightCard.stories.tsx @@ -0,0 +1,109 @@ +import { Castle, LockKeyhole, Shield } from "lucide-react" +import { Meta, StoryObj } from "@storybook/nextjs" + +import { CardTitle } from "@/components/ui/card" + +import { HighlightCard, HighlightCardContent, HighlightStack, IconBox } from "." + +const meta = { + title: "Components / Cards / HighlightCard", + component: HighlightCard, + parameters: { + chromatic: { disableSnapshot: true }, + docs: { + description: { + component: + "Composition family for the in-content 'highlight' layout: a coloured `IconBox`, an optional `CardTitle`, and `HighlightCardContent` body, optionally stacked through `HighlightStack` to produce the divided list seen on the 'What is Ethereum' / 'What is Ether' pages. None of the parts are linkable on their own -- this is purely a content layout.", + }, + }, + }, +} satisfies Meta + +export default meta + +type Story = StoryObj + +export const Default: Story = { + render: () => ( + + + + +
+ Censorship resistance + +

+ No government or company has control over Ethereum. Decentralization + makes it nearly impossible for anyone to stop you from receiving + payments or using services on Ethereum. +

+
+
+
+ ), +} + +export const Stack: Story = { + render: () => ( + + + + + +
+ Censorship resistance + +

+ No single entity can stop you from sending value or interacting + with applications on Ethereum. +

+
+
+
+ + + + +
+ Strong security guarantees + +

+ Ethereum is secured by hundreds of thousands of validators + distributed worldwide. +

+
+
+
+ + + + +
+ Reliability + +

+ The network has run continuously since 2015 and is designed for + long-term operation. +

+
+
+
+
+ ), +} + +export const IconBoxOnly: Story = { + render: () => ( +
+ + + + + + + + + +
+ ), +} diff --git a/src/components/HorizontalCard/HorizontalCard.stories.tsx b/src/components/HorizontalCard/HorizontalCard.stories.tsx new file mode 100644 index 00000000000..fa5f2e4283c --- /dev/null +++ b/src/components/HorizontalCard/HorizontalCard.stories.tsx @@ -0,0 +1,81 @@ +import { Meta, StoryObj } from "@storybook/nextjs" + +import { ButtonLink } from "@/components/ui/buttons/Button" +import { VStack } from "@/components/ui/flex" + +import HorizontalCard from "." + +const meta = { + title: "Components / Cards / HorizontalCard", + component: HorizontalCard, + parameters: { + chromatic: { disableSnapshot: true }, + docs: { + description: { + component: + "Borderless horizontal item with a large emoji on the left and a title + description on the right. Used in vertical lists where each row needs a glanceable icon, and the surrounding container provides the visual grouping (no card chrome of its own).", + }, + }, + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], +} satisfies Meta + +export default meta + +type Story = StoryObj + +export const Default: Story = { + args: { + emoji: ":fuel_pump:", + title: "Gas fees", + description: + "The cost of doing an action on Ethereum, paid in ETH to validators.", + }, +} + +export const WithoutTitle: Story = { + args: { + emoji: ":dollar:", + description: + "USDC is a fully-reserved stablecoin pegged 1:1 to the US dollar.", + }, +} + +export const WithChildren: Story = { + args: { + emoji: ":wallet:", + title: "Self-custody wallet", + description: + "A wallet you control directly, with no third party holding your keys.", + children: ( + + Find a wallet + + ), + }, +} + +export const TokenList = { + render: () => ( + + + + + + ), +} diff --git a/src/components/HorizontalCard.tsx b/src/components/HorizontalCard/index.tsx similarity index 95% rename from src/components/HorizontalCard.tsx rename to src/components/HorizontalCard/index.tsx index 0c76e1e7e38..bead5a9fb95 100644 --- a/src/components/HorizontalCard.tsx +++ b/src/components/HorizontalCard/index.tsx @@ -1,8 +1,8 @@ import React, { ReactNode } from "react" -import { cn } from "@/lib/utils/cn" +import Emoji from "@/components/Emoji" -import Emoji from "./Emoji" +import { cn } from "@/lib/utils/cn" export interface HorizontalCardProps extends Omit, "title"> { diff --git a/src/components/SubpageCard/SubpageCard.stories.tsx b/src/components/SubpageCard/SubpageCard.stories.tsx new file mode 100644 index 00000000000..50e71ba3546 --- /dev/null +++ b/src/components/SubpageCard/SubpageCard.stories.tsx @@ -0,0 +1,95 @@ +import { Code, Compass, GraduationCap, Hammer, Map, Wrench } from "lucide-react" +import { Meta, StoryObj } from "@storybook/nextjs" + +import SubpageCard from "." + +const meta = { + title: "Components / Cards / SubpageCard", + component: SubpageCard, + parameters: { + chromatic: { disableSnapshot: true }, + docs: { + description: { + component: + "Linkable card with an icon, title and description. The entire card is a link via `LinkBox` / `LinkOverlay`. Pass `inlineLink` to surface a visible CTA inside the card; otherwise the card body acts as the click target invisibly.", + }, + }, + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], +} satisfies Meta + +export default meta + +type Story = StoryObj + +export const Default: Story = { + args: { + title: "Frameworks", + description: + "Pre-built tools that handle a lot of the heavy lifting for building a decentralized application.", + icon: , + href: "/developers/tools/frameworks/", + }, +} + +export const WithInlineLink: Story = { + args: { + title: "The Ethereum roadmap", + description: + "A look at how Ethereum is being improved over time, and the priorities driving those upgrades.", + icon: , + href: "/roadmap/", + inlineLink: { + text: "See the roadmap", + }, + }, +} + +export const ToolsPageGrid = { + render: () => ( +
+ } + href="/developers/tools/frameworks/" + /> + } + href="/developers/tools/ides/" + /> + } + href="/developers/tools/block-explorers/" + /> + } + href="/developers/tools/testing/" + /> + } + href="/developers/tutorials/" + /> + } + href="/roadmap/" + /> +
+ ), +} diff --git a/src/components/SubpageCard.tsx b/src/components/SubpageCard/index.tsx similarity index 100% rename from src/components/SubpageCard.tsx rename to src/components/SubpageCard/index.tsx