diff --git a/.all-contributorsrc b/.all-contributorsrc index 892a491d7ba..43daaa81953 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -12398,6 +12398,87 @@ "contributions": [ "content" ] + }, + { + "login": "yannggg", + "name": "Yann Gerardi", + "avatar_url": "https://avatars.githubusercontent.com/u/29145014?v=4", + "profile": "https://www.mtpelerin.com", + "contributions": [ + "code" + ] + }, + { + "login": "panditdhamdhere", + "name": "Pandit Dhamdhere", + "avatar_url": "https://avatars.githubusercontent.com/u/85685981?v=4", + "profile": "https://github.com/panditdhamdhere", + "contributions": [ + "code" + ] + }, + { + "login": "ferreiramarcelo", + "name": "M@rC3L0", + "avatar_url": "https://avatars.githubusercontent.com/u/11818521?v=4", + "profile": "http://linkedin.com/in/fmarcelo", + "contributions": [ + "ideas" + ] + }, + { + "login": "Refdeveloper", + "name": "DevOFtoken", + "avatar_url": "https://avatars.githubusercontent.com/u/87125773?v=4", + "profile": "https://github.com/Refdeveloper", + "contributions": [ + "ideas" + ] + }, + { + "login": "vvvvvv1vvvvvv", + "name": "vvvvvv1vvvvvv", + "avatar_url": "https://avatars.githubusercontent.com/u/86296331?v=4", + "profile": "https://github.com/vvvvvv1vvvvvv", + "contributions": [ + "doc" + ] + }, + { + "login": "abraj", + "name": "Abhishek Raj", + "avatar_url": "https://avatars.githubusercontent.com/u/8170980?v=4", + "profile": "https://raj.me", + "contributions": [ + "content" + ] + }, + { + "login": "p1kalys", + "name": "Pavan Emani", + "avatar_url": "https://avatars.githubusercontent.com/u/85685112?v=4", + "profile": "https://github.com/p1kalys", + "contributions": [ + "content" + ] + }, + { + "login": "makoshan", + "name": "Mako Shan", + "avatar_url": "https://avatars.githubusercontent.com/u/7024451?v=4", + "profile": "https://v2eth.com/", + "contributions": [ + "content" + ] + }, + { + "login": "cratiu222", + "name": "Christina", + "avatar_url": "https://avatars.githubusercontent.com/u/156356273?v=4", + "profile": "https://github.com/cratiu222", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 7, diff --git a/.github/workflows/wallets-check.yml b/.github/workflows/wallets-check.yml new file mode 100644 index 00000000000..952b398a887 --- /dev/null +++ b/.github/workflows/wallets-check.yml @@ -0,0 +1,32 @@ +name: Check Wallet Data for new_to_crypto + +on: + pull_request: + paths: + - "src/data/wallets/wallet-data.ts" + +jobs: + check_new_to_crypto: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Check for new_to_crypto + id: check_new_to_crypto + run: | + if git diff --name-only HEAD^ | grep -q "data/wallets/wallet-data.ts"; then + if git diff -U0 HEAD^ HEAD data/wallets/wallet-data.ts | grep -q "+.*new_to_crypto: true"; then + echo "New wallet added with 'new_to_crypto: true'" + echo "new_to_crypto_found=true" >> $GITHUB_OUTPUT + else + echo "new_to_crypto_found=false" >> $GITHUB_OUTPUT + fi + fi + + - name: Comment on PR if new_to_crypto is added + if: steps.check_new_to_crypto.outputs.new_to_crypto_found == 'true' + uses: actions-ecosystem/action-add-comment@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + body: "A wallet has been added with 'new_to_crypto: true'. Please ensure this meets our guidelines." diff --git a/.storybook/i18next.ts b/.storybook/i18next.ts index 7e62b64b3ba..9915d3be8d8 100644 --- a/.storybook/i18next.ts +++ b/.storybook/i18next.ts @@ -22,6 +22,7 @@ export const ns = [ "page-developers-index", "page-what-is-ethereum", "page-upgrades-index", + "page-developers-docs", ] as const const supportedLngs = Object.keys(baseLocales) diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 35b8b477d9e..191fa7888d7 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -1,17 +1,19 @@ import isChromatic from "chromatic/isChromatic" import { MotionGlobalConfig } from "framer-motion" -import { withThemeByDataAttribute } from "@storybook/addon-themes" import type { Preview } from "@storybook/react" import ThemeProvider from "@/components/ThemeProvider" +import { TooltipProvider } from "@/components/ui/tooltip" import i18n, { baseLocales } from "./i18next" +import { withNextThemes } from "./withNextThemes" -import "@docsearch/css" import "../src/styles/global.css" import "../src/styles/fonts.css" import "../src/styles/docsearch.css" +import "@docsearch/css" + MotionGlobalConfig.skipAnimations = isChromatic() export const breakpointSet: [token: string, value: string][] = [ @@ -29,7 +31,7 @@ const preview: Preview = { locales: baseLocales, }, decorators: [ - withThemeByDataAttribute({ + withNextThemes({ themes: { light: "light", dark: "dark", @@ -38,7 +40,9 @@ const preview: Preview = { }), (Story) => ( - + + + ), ], diff --git a/.storybook/withNextThemes.ts b/.storybook/withNextThemes.ts new file mode 100644 index 00000000000..236c3a51982 --- /dev/null +++ b/.storybook/withNextThemes.ts @@ -0,0 +1,28 @@ +import { useEffect } from "react" +import { useTheme } from "next-themes" +import { + type DataAttributeStrategyConfiguration, + DecoratorHelpers, +} from "@storybook/addon-themes" +import type { Decorator } from "@storybook/react/*" + +const { initializeThemeState, pluckThemeFromContext } = DecoratorHelpers + +export const withNextThemes = ({ + themes, + defaultTheme, +}: DataAttributeStrategyConfiguration): Decorator => { + initializeThemeState(Object.keys(themes), defaultTheme) + + return (getStory, context) => { + const selectedTheme = pluckThemeFromContext(context) + const selected = selectedTheme || defaultTheme + const { setTheme } = useTheme() + + useEffect(() => { + setTheme(selected) + }, [selected, setTheme]) + + return getStory(context) + } +} diff --git a/README.md b/README.md index e18ef9f352d..782c6ed0cfe 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ [![Netlify Status](https://api.netlify.com/api/v1/badges/e8f2e766-888b-4954-8500-1b647d84db99/deploy-status)](https://app.netlify.com/sites/ethereumorg/deploys) [![All Contributors](https://img.shields.io/github/all-contributors/ethereum/ethereum-org-website?color=orange&style=flat-square)](#contributors-) [![Discord](https://img.shields.io/discord/714888181740339261?color=1C1CE1&label=ethereum.org%20%7C%20Discord%20%F0%9F%91%8B%20&style=flat-square)](https://discord.gg/ethereum-org) -[![Twitter Follow](https://img.shields.io/twitter/follow/ethdotorg.svg?style=social)](https://twitter.com/ethdotorg) +[![Twitter Follow](https://img.shields.io/twitter/follow/ethdotorg.svg?style=social)](https://x.com/ethdotorg) [![Crowdin](https://badges.crowdin.net/ethereum-org/localized.svg)](https://crowdin.com/project/ethereum-org) [![gitpoap badge](https://public-api.gitpoap.io/v1/repo/ethereum/ethereum-org-website/badge)](https://www.gitpoap.io/gh/ethereum/ethereum-org-website) @@ -1896,6 +1896,19 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Sanjana
Sanjana

🐛 Zaryab
Zaryab

🖋 + + Yann Gerardi
Yann Gerardi

💻 + Pandit Dhamdhere
Pandit Dhamdhere

💻 + M@rC3L0
M@rC3L0

🤔 + DevOFtoken
DevOFtoken

🤔 + vvvvvv1vvvvvv
vvvvvv1vvvvvv

📖 + Abhishek Raj
Abhishek Raj

🖋 + Pavan Emani
Pavan Emani

🖋 + + + Mako Shan
Mako Shan

🖋 + Christina
Christina

🖋 + diff --git a/package.json b/package.json index fa1080955bc..eba33f3d5b1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ethereum-org-website", - "version": "8.9.0", + "version": "8.10.1", "license": "MIT", "private": true, "scripts": { @@ -46,6 +46,7 @@ "@radix-ui/react-radio-group": "^1.2.0", "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-switch": "^1.1.0", + "@radix-ui/react-tooltip": "^1.1.2", "@radix-ui/react-visually-hidden": "^1.1.0", "@sentry/nextjs": "^8.19.0", "@socialgouv/matomo-next": "^1.8.0", @@ -63,7 +64,6 @@ "lodash.merge": "^4.6.2", "lodash.shuffle": "^4.2.0", "lodash.union": "^4.6.0", - "lucide-react": "^0.400.0", "next": "^14.2.3", "next-i18next": "^14.0.3", "next-mdx-remote": "^3.0.8", @@ -82,6 +82,7 @@ "react-select": "5.8.0", "reading-time": "^1.5.0", "remark-gfm": "^3.0.1", + "swiper": "^11.1.10", "tailwind-merge": "^2.3.0", "tailwindcss-animate": "^1.0.7", "usehooks-ts": "^3.1.0", @@ -104,6 +105,8 @@ "@types/node": "^20.4.2", "@types/react": "18.2.57", "@types/react-dom": "18.2.19", + "@types/swiper": "^6.0.0", + "@types/xml2js": "^0.4.14", "@typescript-eslint/eslint-plugin": "^6.19.0", "@typescript-eslint/parser": "^6.19.0", "autoprefixer": "^10.4.19", @@ -133,7 +136,8 @@ "tsconfig-paths-webpack-plugin": "4.1.0", "typescript": "^5.5.2", "unified": "^10.0.0", - "unist-util-visit": "^5.0.0" + "unist-util-visit": "^5.0.0", + "xml2js": "^0.6.2" }, "resolutions": { "jackspeak": "2.1.1", diff --git a/public/content/bridges/index.md b/public/content/bridges/index.md index 163097ada11..26d47630c24 100644 --- a/public/content/bridges/index.md +++ b/public/content/bridges/index.md @@ -95,6 +95,15 @@ Many bridging solutions adopt models between these two extremes with varying deg +## Use bridge {#use-bridge} + +Using bridges allows you to move your assets across different blockchains. Here are some resources that can help you find and use bridges: + +- **[L2BEAT Bridges Summary](https://l2beat.com/bridges/summary) & [L2BEAT Bridges Risk Analysis](https://l2beat.com/bridges/risk)**: A comprehensive summary of various bridges, including details on market share, bridge type, and destination chains. L2BEAT also has a risk analysis for bridges, helping users make informed decisions when selecting a bridge. +- **[DefiLlama Bridge Summary](https://defillama.com/bridges/Ethereum)**: A summary of bridge volumes across Ethereum networks. + + + ## Risk using bridges {#bridge-risk} Bridges are in the early stages of development. It is likely that the optimal bridge design has not yet been discovered. Interacting with any type of bridge carries risk: diff --git a/public/content/community/support/index.md b/public/content/community/support/index.md index 74ea217756e..c611b14165e 100644 --- a/public/content/community/support/index.md +++ b/public/content/community/support/index.md @@ -100,5 +100,6 @@ Most of the teams building Ethereum clients also have dedicated, public-facing, - [Lighthouse](https://discord.gg/cyAszAh) - [Teku](https://discord.gg/7hPv2T6) - [Lodestar](https://discord.gg/aMxzVcr) +- [Grandine](https://discord.gg/H9XCdUSyZd) You can also [learn how to run a node here](/developers/docs/nodes-and-clients/run-a-node/). diff --git a/public/content/contributing/adding-staking-products/index.md b/public/content/contributing/adding-staking-products/index.md index fb8fd48e02d..3444c30ed2d 100644 --- a/public/content/contributing/adding-staking-products/index.md +++ b/public/content/contributing/adding-staking-products/index.md @@ -87,7 +87,7 @@ For any custom software or smart contracts involved: For software products related to node or client setup, management or migration: -**Which consensus layer clients (i.e. Lighthouse, Teku, Nimbus, Prysm) are supported?** +**Which consensus layer clients (i.e. Lighthouse, Teku, Nimbus, Prysm, Grandine) are supported?** - Which clients are supported? Can the user choose? - This is used to determine the products "multi-client" score. diff --git a/public/content/contributing/translation-program/translatathon/details/index.md b/public/content/contributing/translation-program/translatathon/details/index.md index 03fcc848f77..ce2adeda880 100644 --- a/public/content/contributing/translation-program/translatathon/details/index.md +++ b/public/content/contributing/translation-program/translatathon/details/index.md @@ -154,7 +154,7 @@ All translations will also be subject to a thorough review before being added to -

You can translate into any language! It is recommended to only translate into your native language to ensure sufficient quality, but in short, all language available in Crowdin are in scope for the Translatathon.

+

You can translate into any language! It is recommended to only translate into your native language to ensure sufficient quality, but in short, all languages available in Crowdin are in scope for the Translatathon.

If you want to translate into a language that isn't available in Crowdin, reach out to us and we will add any language per request.

diff --git a/public/content/cookie-policy/index.md b/public/content/cookie-policy/index.md index b98d3b4511f..28c63f54079 100644 --- a/public/content/cookie-policy/index.md +++ b/public/content/cookie-policy/index.md @@ -9,7 +9,7 @@ hideEditButton: true Our Websites may use cookies to distinguish you from other users of our Websites. This may help us to provide you with a good experience when you browse our Websites and may also allow us to improve our Websites. By continuing to browse the Websites, you are agreeing to our use of cookies as well as the terms of this policy (the “Cookie Policy”). A cookie is a small file of letters and numbers that we may store on your browser or the hard drive of your computer if you agree. Cookies contain information that is transferred to your computer's hard drive. We may use the following cookies: - **Strictly necessary cookies.** These are cookies that are required for the operation of our websites. They include, for example, cookies that enable you to log into secure areas of our websites, use a shopping cart or make use of e-commerce payment processing services. -- **Analytical/performance cookies.** They allow us to recognise and count the number of visitors and to see how visitors move around our websites when they are using it. This may help us to improve the way our websites work, for example, by ensuring that users are finding what they are looking for easily. +- **Analytical/performance cookies.** They allow us to recognise and count the number of visitors and to see how visitors move around our websites when they are using them. This may help us to improve the way our websites work, for example, by ensuring that users are finding what they are looking for easily. - **Functionality cookies.** These cookies are used to recognise you when you return to our websites. They may enable us to personalise our content for you, greet you by name, or remember your preferences (for example, your choice of language or region). - **Targeting cookies.** These cookies record your visit to our websites, the pages you have visited and the links you have followed. diff --git a/public/content/developers/docs/consensus-mechanisms/pos/index.md b/public/content/developers/docs/consensus-mechanisms/pos/index.md index 8101b4a54aa..bb495045c8a 100644 --- a/public/content/developers/docs/consensus-mechanisms/pos/index.md +++ b/public/content/developers/docs/consensus-mechanisms/pos/index.md @@ -24,7 +24,7 @@ Whereas under proof-of-work, the timing of blocks is determined by the mining di The following provides an end-to-end explanation of how a transaction gets executed in Ethereum proof-of-stake. -1. A user creates and signs a [transaction](/developers/docs/transactions/) with their private key. This is usually handled by a wallet or a library such as [ether.js](https://docs.ethers.io/v5/), [web3js](https://docs.web3js.org/), [web3py](https://web3py.readthedocs.io/en/v5/) etc but under the hood the user is making a request to a node using the Ethereum [JSON-RPC API](/developers/docs/apis/json-rpc/). The user defines the amount of gas that they are prepared to pay as a tip to a validator to encourage them to include the transaction in a block. The [tips](/developers/docs/gas/#priority-fee) get paid to the validator while the [base fee](/developers/docs/gas/#base-fee) gets burned. +1. A user creates and signs a [transaction](/developers/docs/transactions/) with their private key. This is usually handled by a wallet or a library such as [ethers.js](https://docs.ethers.org/v6/), [web3js](https://docs.web3js.org/), [web3py](https://web3py.readthedocs.io/en/v5/) etc but under the hood the user is making a request to a node using the Ethereum [JSON-RPC API](/developers/docs/apis/json-rpc/). The user defines the amount of gas that they are prepared to pay as a tip to a validator to encourage them to include the transaction in a block. The [tips](/developers/docs/gas/#priority-fee) get paid to the validator while the [base fee](/developers/docs/gas/#base-fee) gets burned. 2. The transaction is submitted to an Ethereum [execution client](/developers/docs/nodes-and-clients/#execution-client) which verifies its validity. This means ensuring that the sender has enough ETH to fulfill the transaction and they have signed it with the correct key. 3. If the transaction is valid, the execution client adds it to its local mempool (list of pending transactions) and also broadcasts it to other nodes over the execution layer gossip network. When other nodes hear about the transaction they add it to their local mempool too. Advanced users might refrain from broadcasting their transaction and instead forward it to specialized block builders such as [Flashbots Auction](https://docs.flashbots.net/flashbots-auction/overview). This allows them to organize the transactions in upcoming blocks for maximum profit ([MEV](/developers/docs/mev/#mev-extraction)). 4. One of the validator nodes on the network is the block proposer for the current slot, having previously been selected pseudo-randomly using RANDAO. This node is responsible for building and broadcasting the next block to be added to the Ethereum blockchain and updating the global state. The node is made up of three parts: an execution client, a consensus client and a validator client. The execution client bundles transactions from the local mempool into an "execution payload" and executes them locally to generate a state change. This information is passed to the consensus client where the execution payload is wrapped as part of a "beacon block" that also contains information about rewards, penalties, slashings, attestations etc. that enable the network to agree on the sequence of blocks at the head of the chain. The communication between the execution and consensus clients is described in more detail in [Connecting the Consensus and Execution Clients](/developers/docs/networking-layer/#connecting-clients). diff --git a/public/content/developers/docs/design-and-ux/dex-design-best-practice/index.md b/public/content/developers/docs/design-and-ux/dex-design-best-practice/index.md index ac9d92c55b6..5d4fc3f0e59 100644 --- a/public/content/developers/docs/design-and-ux/dex-design-best-practice/index.md +++ b/public/content/developers/docs/design-and-ux/dex-design-best-practice/index.md @@ -5,7 +5,7 @@ lang: en --- Since the launch of Uniswap in 2018, there have been hundreds of decentralized exchanges launched across dozens of different chains. -Many of these have introduced new elements or added their own twist, but the interface has remained generally the same. +Many of these introduced new elements or added their own twist, but the interface has remained generally the same. One reason for this is [Jakob’s Law](https://lawsofux.com/jakobs-law/): diff --git a/public/content/developers/docs/design-and-ux/heuristics-for-web3/index.md b/public/content/developers/docs/design-and-ux/heuristics-for-web3/index.md index 4b625c268d1..41d3a665fd7 100644 --- a/public/content/developers/docs/design-and-ux/heuristics-for-web3/index.md +++ b/public/content/developers/docs/design-and-ux/heuristics-for-web3/index.md @@ -5,7 +5,7 @@ lang: en --- Usability heuristics are broad “rules of thumb” that you can use to measure the usability of your site. -These heuristics are specifically tailored for Web3 and should be used alongside Jakob Nielsen's [10 general principles for interaction design](https://www.nngroup.com/articles/ten-usability-heuristics/). +The 7 heuristics here are specifically tailored for Web3 and should be used alongside Jakob Nielsen's [10 general principles for interaction design](https://www.nngroup.com/articles/ten-usability-heuristics/). ## Seven usability heuristics for web3 {#seven-usability-heuristics-for-web3} diff --git a/public/content/developers/docs/design-and-ux/index.md b/public/content/developers/docs/design-and-ux/index.md index 2f4452cc009..ffbe988eeb8 100644 --- a/public/content/developers/docs/design-and-ux/index.md +++ b/public/content/developers/docs/design-and-ux/index.md @@ -23,11 +23,13 @@ This is a curated list of user research done in web3 that may help with design a | Area of focus | Name | | :------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Crypto onboarding | [The WalletConnect Pulse 2024: Crypto Consumer Sentiment & Usage](https://walletconnect.com/pulse-2024-crypto-consumer-report) | | Crypto onboarding | [CRADL: UX in Cryptocurrency](https://docs.google.com/presentation/d/1s2OPSH5sMJzxRYaJSSRTe8W2iIoZx0PseIV-WeZWD1s/edit?usp=sharing) | | Crypto onboarding | [CRADL: Onboarding to Cryptocurrency](https://docs.google.com/presentation/d/1R9nFuzA-R6SxaGCKhoMbE4Vxe0JxQSTiHXind3LVq_w/edit?usp=sharing) | | Crypto onboarding | [Bitcoin UX report](https://github.com/patestevao/BitcoinUX-report/blob/master/report.md) | | Crypto onboarding | [ConSensys: The State of Web3 perception around the world 2023](https://consensys.io/insight-report/web3-and-crypto-global-survey-2023) | | Crypto onboarding | [NEAR: Accelerating the journey towards adoption](https://drive.google.com/file/d/1VuaQP4QSaQxR5ddQKTMGI0b0rWdP7uGn/view) | +| Staking | [OpenUX: Rocket Pool Node Operator UX](https://storage.googleapis.com/rocketpool/RocketPool-NodeOperator-UX-Report-Jan-2024.pdf) | | Staking | [Staking: Key trends, takeaways, and predictions - Eth Staker](https://lookerstudio.google.com/u/0/reporting/cafcee00-e1af-4148-bae8-442a88ac75fa/page/p_ja2srdhh2c?s=hmbTWDh9hJo) | | Staking | [Multi App Staking]() | | DAO | [2022 DAO Research Update: What do DAO Builders Need?](https://blog.aragon.org/2022-dao-research-update/) | @@ -48,6 +50,11 @@ This is a curated list of user research done in web3 that may help with design a - [Neueux.com](https://neueux.com/apps) - UI library of user flows with diverse filtering options - [Web3's Usability Crisis: What You NEED to Know!](https://www.youtube.com/watch?v=oBSXT_6YDzg) - A panel discussion on pitfalls of developer focused project building (video, 34 min) +## Getting Started {#getting-started} + +- [Heuristics for Web3](/developers/docs/design-and-ux/heuristics-for-web3/) - 7 heuristics for Web3 interface design +- [DEX Design Best Practices](/developers/docs/design-and-ux/dex-design-best-practice/) - A guide to designing Decentralized Exchanges + ## Web3 Design Case Studies {#design-case-studies} - [Deep Work Studio](https://deepwork.studio/case-studies/) diff --git a/public/content/developers/docs/frameworks/index.md b/public/content/developers/docs/frameworks/index.md index cc75131f7f3..fd16ca308d5 100644 --- a/public/content/developers/docs/frameworks/index.md +++ b/public/content/developers/docs/frameworks/index.md @@ -146,6 +146,14 @@ Before diving into frameworks, we recommend you first read through our introduct - [GitHub](https://github.com/Ackee-Blockchain/wake) - [VS Code Extension](https://marketplace.visualstudio.com/items?itemName=AckeeBlockchain.tools-for-solidity) +**Veramo -** **_Open source, modular and agnostic framework that makes it easy for decentralized application developers to build decentralized identities and verifiable credentials into their applications._** + +- [Homepage](https://veramo.io/) +- [Documentation](https://veramo.io/docs/basics/introduction) +- [GitHub](https://github.com/uport-project/veramo) +- [Discord](https://discord.com/invite/FRRBdjemHV) +- [NPM Package](https://www.npmjs.com/package/@veramo/core) + ## Further reading {#further-reading} _Know of a community resource that helped you? Edit this page and add it!_ diff --git a/public/content/developers/docs/networking-layer/portal-network/index.md b/public/content/developers/docs/networking-layer/portal-network/index.md index 1868533d7cc..b47f6e1ce40 100644 --- a/public/content/developers/docs/networking-layer/portal-network/index.md +++ b/public/content/developers/docs/networking-layer/portal-network/index.md @@ -59,7 +59,13 @@ The benefits of this network design are: The diagram below shows the functions of existing clients that can be delivered by the Portal Network, enabling users to access these functions on very low-resource devices. -![portal network table](portal-network-table2.png) +### The Portal Networks + +| Beacon light client | State network | Transaction gossip | History network | +| ------------------- | ---------------------------- | ------------------- | --------------- | +| Beacon chain light | Account and contract storage | Lightweight mempool | Headers | +| Protocol data | | | Block bodies | +| | | | Receipts | ## Client diversity by default {#client-diversity-as-default} diff --git a/public/content/developers/docs/nodes-and-clients/index.md b/public/content/developers/docs/nodes-and-clients/index.md index deb92087004..d5b8819a231 100644 --- a/public/content/developers/docs/nodes-and-clients/index.md +++ b/public/content/developers/docs/nodes-and-clients/index.md @@ -194,12 +194,13 @@ Learn more about it by reading its [documentation](https://github.com/ethereumjs There are multiple consensus clients (previously known as 'Eth2' clients) to support the [consensus upgrades](/roadmap/beacon-chain/). They are responsible for all consensus-related logic including the fork-choice algorithm, processing attestations and managing [proof-of-stake](/developers/docs/consensus-mechanisms/pos) rewards and penalties. | Client | Language | Operating systems | Networks | -| ------------------------------------------------------------- | ---------- | --------------------- | ----------------------------------------------------------------- | +| ------------------------------------------------------------- | ---------- | --------------------- | ----------------------------------------------------------------- | | | [Lighthouse](https://lighthouse.sigmaprime.io/) | Rust | Linux, Windows, macOS | Beacon Chain, Goerli, Pyrmont, Sepolia, Ropsten, and more | | [Lodestar](https://lodestar.chainsafe.io/) | TypeScript | Linux, Windows, macOS | Beacon Chain, Goerli, Sepolia, Ropsten, and more | | [Nimbus](https://nimbus.team/) | Nim | Linux, Windows, macOS | Beacon Chain, Goerli, Sepolia, Ropsten, and more | | [Prysm](https://docs.prylabs.network/docs/getting-started/) | Go | Linux, Windows, macOS | Beacon Chain, Gnosis, Goerli, Pyrmont, Sepolia, Ropsten, and more | | [Teku](https://consensys.net/knowledge-base/ethereum-2/teku/) | Java | Linux, Windows, macOS | Beacon Chain, Gnosis, Goerli, Sepolia, Ropsten, and more | +| [Grandine](https://docs.grandine.io/) (beta) | Rust | Linux, Windows, macOS | Beacon Chain, Goerli, Sepolia, and more ### Lighthouse {#lighthouse} @@ -233,6 +234,12 @@ Teku offers very flexible deployment options. The beacon node and validator clie Teku is written in Java and is Apache 2.0 licensed. It is developed by the Protocols team at ConsenSys that is also responsible for Besu and Web3Signer. Learn more in [Teku docs](https://docs.teku.consensys.net/en/latest/). +### Grandine {#grandine} + +Grandine is a consensus client implementation, written in Rust under the GPL-3.0 license. It is maintained by the Grandine Core Team and is fast, high-performance and lightweight. It fits a wide range of stakers from solo stakers running on low-resource devices such as Raspberry Pi to large institutional stakers running tens of thousands of validators. + +Documentation can be found in the [Grandine Book](https://docs.grandine.io/) + ## Synchronization modes {#sync-modes} To follow and verify current data in the network, the Ethereum client needs to sync with the latest network state. This is done by downloading data from peers, cryptographically verifying their integrity, and building a local blockchain database. diff --git a/public/content/developers/docs/storage/index.md b/public/content/developers/docs/storage/index.md index 104b88274b9..ce2913058bf 100644 --- a/public/content/developers/docs/storage/index.md +++ b/public/content/developers/docs/storage/index.md @@ -45,7 +45,7 @@ Platforms with contract-based persistence: - [Filecoin](https://docs.filecoin.io/about-filecoin/what-is-filecoin/) - [Skynet](https://siasky.net/) - [Storj](https://storj.io/) -- [0Chain](https://0chain.net/) +- [Züs](https://zus.network/) - [Crust Network](https://crust.network) - [Swarm](https://www.ethswarm.org/) - [4EVERLAND](https://www.4everland.org/) @@ -75,7 +75,7 @@ One of the most popular ways to make sure data is retained, is to use some type Types of dStorage with a challenge mechanism: -- 0Chain +- Züs - Skynet - Arweave - Filecoin @@ -88,7 +88,6 @@ There aren't great tools to measure the level of decentralization of platforms, Decentralized tools without KYC: -- 0Chain (implementing a non-KYC edition) - Skynet - Arweave - Filecoin @@ -110,7 +109,7 @@ Proof-of-stake based: - Ethereum - Filecoin -- 0Chain +- Züs - Crust Network ## Related tools {#related-tools} @@ -145,10 +144,10 @@ Proof-of-stake based: - [Documentation](https://docs.arweave.org/info/) - [Arweave](https://github.com/ArweaveTeam/arweave/) -**0chain - _0Chain is a proof-of-stake dStorage platform with sharding and blobbers._** +**Züs - _Züs is a proof-of-stake dStorage platform with sharding and blobbers._** -- [0Chain.net](https://0chain.net/) -- [Documentation](https://docs.0chain.net/0chain/) +- [zus.network](https://zus.network/) +- [Documentation](https://0chaindocs.gitbook.io/zus-docs) - [GitHub](https://github.com/0chain/) **Crust Network - _Crust is a dStorage platform on top of the IPFS._** diff --git a/public/content/governance/index.md b/public/content/governance/index.md index c6abdc919d1..9909b03a88b 100644 --- a/public/content/governance/index.md +++ b/public/content/governance/index.md @@ -48,7 +48,7 @@ There are various stakeholders in the [Ethereum community](/community/), each pl - **Node Operators**: these people run nodes that propagate blocks and transactions, rejecting any invalid transaction or block that they come across. [More on nodes](/developers/docs/nodes-and-clients/). - **EIP Authors**: these people propose changes to the Ethereum protocol, in the form of Ethereum Improvement Proposals (EIPs). [More on EIPs](/eips/). - **Validators**: these people run nodes that can add new blocks to the Ethereum blockchain. -- **Protocol Developers** (a.k.a. "Core Developers" ): these people maintain the various Ethereum implementations (e.g. go-ethereum, Nethermind, Besu, Erigon, Reth at the execution layer or Prysm, Lighthouse, Nimbus, Teku, Lodestar at the consensus layer). [More on Ethereum clients](/developers/docs/nodes-and-clients/). +- **Protocol Developers** (a.k.a. "Core Developers" ): these people maintain the various Ethereum implementations (e.g. go-ethereum, Nethermind, Besu, Erigon, Reth at the execution layer or Prysm, Lighthouse, Nimbus, Teku, Lodestar, Grandine at the consensus layer). [More on Ethereum clients](/developers/docs/nodes-and-clients/). _Note: any individual can be part of multiple of these groups (e.g. a protocol developer could champion an EIP, and run a beacon chain validator, and use DeFi applications). For conceptual clarity, it is easiest to distinguish between them, though._ diff --git a/public/content/staking/solo/index.md b/public/content/staking/solo/index.md index 87c7c55f95d..5b4a0ba5cf0 100644 --- a/public/content/staking/solo/index.md +++ b/public/content/staking/solo/index.md @@ -1,6 +1,6 @@ --- -title: Solo stake your ETH -description: An overview of how to get started solo staking your ETH +title: Home stake your ETH +description: An overview of how to get started home staking your ETH lang: en template: staking emoji: ":money_with_wings:" @@ -13,31 +13,31 @@ summaryPoints: - Remove trust, and never give up control of the keys to your funds --- -## What is solo staking? {#what-is-solo-staking} +## What is home staking? {#what-is-solo-staking} -Solo staking is the act of [running an Ethereum node](/run-a-node/) connected to the internet and depositing 32 ETH to activate a [validator](#faq), giving you the ability to participate directly in network consensus. +Home staking is the act of [running an Ethereum node](/run-a-node/) connected to the internet and depositing 32 ETH to activate a [validator](#faq), giving you the ability to participate directly in network consensus. -**Solo staking increases the decentralization of the Ethereum network**, making Ethereum more censorship-resistant and robust against attacks. Other staking methods may not help the network in the same ways. Solo staking is the best staking option for securing Ethereum. +**Home staking increases the decentralization of the Ethereum network**, making Ethereum more censorship-resistant and robust against attacks. Other staking methods may not help the network in the same ways. Home staking is the best staking option for securing Ethereum. An Ethereum node consists of both an execution layer (EL) client, as well as a consensus layer (CL) client. These clients are software that work together, along with a valid set of signing keys, to verify transactions and blocks, attest to the correct head of the chain, aggregate attestations, and propose blocks. -Solo stakers are responsible for operating the hardware needed to run these clients. It is highly recommended to use a dedicated machine for this that you operate from home–this is extremely beneficial to the health of the network. +Home stakers are responsible for operating the hardware needed to run these clients. It is highly recommended to use a dedicated machine for this that you operate from home–this is extremely beneficial to the health of the network. -A solo staker receives rewards directly from the protocol for keeping their validator properly functioning and online. +A home staker receives rewards directly from the protocol for keeping their validator properly functioning and online. -## Why stake solo? {#why-stake-solo} +## Why stake home? {#why-stake-solo} -Solo staking comes with more responsibility but provides you with maximum control over your funds and staking setup. +Home staking comes with more responsibility but provides you with maximum control over your funds and staking setup. - + -## Considerations before staking solo {#considerations-before-staking-solo} +## Considerations before home staking {#considerations-before-staking-solo} -As much as we wish that solo staking was accessible and risk free to everyone, this is not reality. There are some practical and serious considerations to keep in mind before choosing to solo stake your ETH. +As much as we wish that home staking was accessible and risk free to everyone, this is not reality. There are some practical and serious considerations to keep in mind before choosing to home stake your ETH. @@ -93,7 +93,7 @@ The Staking Launchpad is an open source application that will help you become a ## What to consider with node and client setup tools {#node-tool-considerations} -There are a growing number of tools and services to help you solo stake your ETH, but each come with different risks and benefits. +There are a growing number of tools and services to help you home stake your ETH, but each come with different risks and benefits. Attribute indicators are used below to signal notable strengths or weaknesses a listed staking tool may have. Use this section as a reference for how we define these attributes while you’re choosing what tools to help with your staking journey. @@ -119,7 +119,7 @@ These tools can be used as an alternative to the [Staking Deposit CLI](https://g Have a suggestion for a staking tool we missed? Check out our [product listing policy](/contributing/adding-staking-products/) to see if it would be a good fit, and to submit it for review. -## Explore solo staking guides {#staking-guides} +## Explore home staking guides {#staking-guides} @@ -138,7 +138,7 @@ Each key-pair associated with a validator requires exactly 32 ETH to be activat Do not deposit more than 32 ETH for a single validator. It will not increase your rewards. If a withdrawal address has been set for the validator, excess funds over 32 ETH will be automatically withdrawn to this address during the next validator sweep. -If solo staking seems too demanding for you, consider using a staking-as-a-service provider, or if you're working with less than 32 ETH, check out the staking pools. +If home staking seems too demanding for you, consider using a staking-as-a-service provider, or if you're working with less than 32 ETH, check out the staking pools. diff --git a/public/content/translations/de/defi/index.md b/public/content/translations/de/defi/index.md index 67ccc8c1cc6..2ea94dbade5 100644 --- a/public/content/translations/de/defi/index.md +++ b/public/content/translations/de/defi/index.md @@ -334,7 +334,7 @@ DeFi ist eine Open-Source-Bewegung. DeFi-Protokolle und -Anwendungen sind für j Mehr zum Erstellen von dApps -## Weiterführende Informationen {#futher-reading} +## Weiterführende Informationen {#further-reading} ### DeFi-Daten {#defi-data} diff --git a/public/content/translations/de/refi/index.md b/public/content/translations/de/refi/index.md index 16302e24084..d81e8ae14aa 100644 --- a/public/content/translations/de/refi/index.md +++ b/public/content/translations/de/refi/index.md @@ -20,7 +20,7 @@ Stattdessen zielt ReFi darauf ab, ökologische, kommunale oder soziale Probleme Eine der Grundlagen von ReFi ist das Konzept der regenerativen Wirtschaft, das von John Fullerton vom Capital Institute erdacht wurde. Er schlug [acht miteinander verknüpfte Grundsätze](https://capitalinstitute.org/8-principles-regenerative-economy/) vor, die der systemischen Gesundheit zugrunde liegen: -![Acht miteinander verknüpfte Grundsätze](./refi-regenerative-economy-diagram.png) +![Acht miteinander verknüpfte Grundsätze](refi-regenerative-economy-diagram.png) ReFi-Projekte verwirklichen diese Prinzipien mithilfe von [Smart Contracts](/glossary/#smart-contract) und [dezentralen Finanzen (DeFi)](/glossary/#defi), um Anreize für regenerative Verhaltensweisen zu schaffen, z. B. für die Wiederherstellung geschädigter Ökosysteme, und die Zusammenarbeit in großem Maßstab bei globalen Problemen wie Klimawandel und Verlust der Artenvielfalt zu erleichtern. diff --git a/public/content/translations/el/defi/index.md b/public/content/translations/el/defi/index.md index 185cb8b2904..b2ce7f1af30 100644 --- a/public/content/translations/el/defi/index.md +++ b/public/content/translations/el/defi/index.md @@ -334,7 +334,7 @@ H DeFi είναι ένας γενικός όρος για οικονομικά Περισσότερα για τη δημιουργία dapp -## Περισσότερες πληροφορίες {#futher-reading} +## Περισσότερες πληροφορίες {#further-reading} ### Δεδομένα DeFi {#defi-data} diff --git a/public/content/translations/el/desci/index.md b/public/content/translations/el/desci/index.md index b001d273d12..81c80b5f26d 100644 --- a/public/content/translations/el/desci/index.md +++ b/public/content/translations/el/desci/index.md @@ -82,7 +82,7 @@ summaryPoint3: Δημιουργία πάνω στο ανοιχτό επιστη Τα επιστημονικά δεδομένα μπορούν να γίνουν πολύ πιο προσιτά χρησιμοποιώντας τα μοτίβα Web3 και την κατανεμημένη αποθήκευση επιτρέποντας στην έρευνα να επιβιώσει από κατακλυσμικά γεγονότα. -Το σημείο εκκίνησης πρέπει να είναι ένα σύστημα προσβάσιμο από οποιαδήποτε αποκεντρωμένη οντότητα που διαθέτει τα κατάλληλα επαληθεύσιμα διαπιστευτήρια. Αυτό επιτρέπει την ασφαλή αναπαραγωγή ευαίσθητων δεδομένων από αξιόπιστα μέρη, επιτρέποντας την αντίσταση στον πλεονασμό και τη λογοκρισία, την αναπαραγωγή των αποτελεσμάτων, ακόμη και τη δυνατότητα πολλά μέρη να συνεργάζονται και να προσθέτουν νέα δεδομένα στο σύνολο. Εμπιστευτικές υπολογιστικές μέθοδοι όπως [«compute-to-data»](https://7wdata.be/predictive-analytics/compute-to-data-using-blockchain-to-decentralize-data-science-and-ai-with-the-ocean-protocol) το παρέχει εναλλακτικούς μηχανισμούς πρόσβασης στην αναπαραγωγή μη επεξεργασμένων δεδομένων, δημιουργώντας αξιόπιστα ερευνητικά περιβάλλοντα για τα πιο ευαίσθητα δεδομένα. Τα αξιόπιστα ερευνητικά περιβάλλοντα έχουν [αναφερθεί από το NHS](https://medium.com/weavechain/whats-in-store-for-the-future-of-healthcare-data-b6398745fbbb) ως μια μελλοντική λύση για το απόρρητο και τη συνεργασία δεδομένων, δημιουργώντας ένα οικοσύστημα όπου οι ερευνητές μπορούν να εργάζονται με ασφάλεια με δεδομένα επιτόπου, χρησιμοποιώντας τυποποιημένα περιβάλλοντα κοινής χρήσης κώδικα και πρακτικών. +Το σημείο εκκίνησης πρέπει να είναι ένα σύστημα προσβάσιμο από οποιαδήποτε αποκεντρωμένη οντότητα που διαθέτει τα κατάλληλα επαληθεύσιμα διαπιστευτήρια. Αυτό επιτρέπει την ασφαλή αναπαραγωγή ευαίσθητων δεδομένων από αξιόπιστα μέρη, επιτρέποντας την αντίσταση στον πλεονασμό και τη λογοκρισία, την αναπαραγωγή των αποτελεσμάτων, ακόμη και τη δυνατότητα πολλά μέρη να συνεργάζονται και να προσθέτουν νέα δεδομένα στο σύνολο. Εμπιστευτικές υπολογιστικές μέθοδοι όπως [«compute-to-data»](https://7wdata.be/predictive-analytics/compute-to-data-using-blockchain-to-decentralize-data-science-and-ai-with-the-ocean-protocol το) παρέχει εναλλακτικούς μηχανισμούς πρόσβασης στην αναπαραγωγή μη επεξεργασμένων δεδομένων, δημιουργώντας αξιόπιστα ερευνητικά περιβάλλοντα για τα πιο ευαίσθητα δεδομένα. Τα αξιόπιστα ερευνητικά περιβάλλοντα έχουν [αναφερθεί από το NHS](https://medium.com/weavechain/whats-in-store-for-the-future-of-healthcare-data-b6398745fbbb) ως μια μελλοντική λύση για το απόρρητο και τη συνεργασία δεδομένων, δημιουργώντας ένα οικοσύστημα όπου οι ερευνητές μπορούν να εργάζονται με ασφάλεια με δεδομένα επιτόπου, χρησιμοποιώντας τυποποιημένα περιβάλλοντα κοινής χρήσης κώδικα και πρακτικών. Οι ευέλικτες λύσεις δεδομένων Web3 υποστηρίζουν τα παραπάνω σενάρια και παρέχουν τη βάση για μια πραγματικά ανοιχτή επιστήμη, όπου οι ερευνητές μπορούν να δημιουργούν δημόσια αγαθά χωρίς άδειες πρόσβασης ή χρεώσεις. Οι λύσεις δημόσιων δεδομένων Web3 όπως το IPFS, το Arweave και το Filecoin έχουν βελτιστοποιηθεί για αποκέντρωση. Το dClimate, για παράδειγμα, παρέχει καθολική πρόσβαση σε δεδομένα για το κλίμα και τον καιρό, μεταξύ άλλων από μετεωρολογικούς σταθμούς και προγνωστικά κλιματικά μοντέλα. diff --git a/public/content/translations/it/contributing/index.md b/public/content/translations/it/contributing/index.md index c7ed0c65aa9..94dcb8ec7fd 100644 --- a/public/content/translations/it/contributing/index.md +++ b/public/content/translations/it/contributing/index.md @@ -88,7 +88,7 @@ Abbiamo una bassa barriera per l'inserimento di ticket sul nostro repository di ## Rivendica il tuo token di traguardo su catena (OAT) {#oat} -Se il tuo contributo viene integrato su ethereum.org, potrai rivendicare un distintivo speciale su [Galxe](https://galxe.com/ethereumorg/). Un token di traguardo su catena (OAT) è la dimostrazione che tu abbia contribuito a rendere l'ecosistema un po' più fantastico. +Se il tuo contributo viene aggiunto a ethereum.org, avrai una possibilità di rivendicare un distintivo speciale su [Galxe](https://app.galxe.com/quest/ethereumorg). Un token di traguardo su catena (OAT) è la dimostrazione che tu abbia contribuito a rendere l'ecosistema un po' più fantastico. [Maggiori informazioni sui OAT](https://help.galxe.com/en/articles/7067290-galxe-oats-reward-and-celebrate-achievements) diff --git a/public/content/translations/it/contributing/translation-program/how-to-translate/index.md b/public/content/translations/it/contributing/translation-program/how-to-translate/index.md index f2dfbfc3cc9..d95e1670b96 100644 --- a/public/content/translations/it/contributing/translation-program/how-to-translate/index.md +++ b/public/content/translations/it/contributing/translation-program/how-to-translate/index.md @@ -70,7 +70,7 @@ Qui puoi trovare i commenti, i suggerimenti della memoria di traduzione e le voc Usando i pulsanti in cima, puoi anche passare alla memoria di traduzione, dove puoi cercare le traduzioni esistenti, o al glossario, che contiene le descrizioni e le traduzioni standard dei termini chiave. -Vuoi saperne di più? Sentiti libero di dare un'occhiata alla [documentazione sull'uso dell'editor online di Crowdin](https://support.crowdin.com/online-editor/) +Vuoi scoprire di più? Sentiti libero di dare un'occhiata alla [documentazione sull'uso dell'editor online di Crowdin](https://support.crowdin.com/online-editor/) ### Processo di revisione {#review-process} diff --git a/public/content/translations/it/decentralized-identity/index.md b/public/content/translations/it/decentralized-identity/index.md index 528d96b474e..2f5c99d66cc 100644 --- a/public/content/translations/it/decentralized-identity/index.md +++ b/public/content/translations/it/decentralized-identity/index.md @@ -73,7 +73,7 @@ L'identità decentralizzata può aiutare a creare delle community online prive d ### 4. Protezione Anti-Sybil {#sybil-protection} -Gli attacchi Sybil si riferiscono a individui che ingannano un sistema nel pensare che siano più persone, per incrementare la propria influenza. Le candidature per la concessione di sovvenzioni che utilizzano il [voto quadratico](/glossary/#quadratic-voting) sono vulnerabili a questi attacchi Sybil perché il valore di una sovvenzione aumenta all'aumentare dei voti ricevuti, incentivando gli utenti a dividere i propri contributi tra più identità. Le identità decentralizzate aiutano a prevenirli, incrementando l'onere su ogni partecipante, per dimostrare che siano realmente umani, sebbene spesso senza dover rilevare informazioni private specifiche. +Le applicazioni di concessione di sovvenzioni che utilizzano il [voto quadratico](/glossary/#quadratic-voting) sono vulnerabili agli [attacchi Sybil](/glossary/#sybil-attack) poiché il valore di una sovvenzione viene incrementato all'aumentare delle persone che votano, incentivando gli utenti a dividere i propri contributi tra più identità. Le identità decentralizzate aiutano a prevenirli, incrementando l'onere su ogni partecipante, per dimostrare che siano realmente umani, sebbene spesso senza dover rilevare informazioni private specifiche. ## Cosa sono le attestazioni? {#what-are-attestations} @@ -163,7 +163,7 @@ Esistono molti progetti ambiziosi che utilizzano Ethereum come base per le soluz - **[Prova di Umanità](https://www.proofofhumanity.id)**: _La Prova di Umanità (o PoH), è un sistema di verifica dell'identità sociale, basato su Ethereum._ - **[BrightID](https://www.brightid.org/)**: _Una rete decentralizzata e open source di identità sociale, ideata per riformare la verifica dell'identità tramite la creazione e analisi di un grafico sociale._ - **[walt.id](https://walt.id)**: _Infrastruttura open source di identità decentralizzata e portafoglio che consente a sviluppatori e organizzazioni di sfruttare l'identità auto-sovrana e gli NFT/SBT._ -- **[Masca](https://masca.io/)**: _Portafoglio open source a identità decentralizzata implementato come MetaMask Snap che consente a utenti e sviluppatori di sfruttare DID e VC._ +- **[Veramo](https://veramo.io/)**: _Un framework di JavaScript che facilita per tutti l'utilizzo di dati verificabili crittograficamente nelle proprie applicazioni._ ## Lettura consigliate {#further-reading} diff --git a/public/content/translations/it/defi/index.md b/public/content/translations/it/defi/index.md index 81f7126d683..062acb1396f 100644 --- a/public/content/translations/it/defi/index.md +++ b/public/content/translations/it/defi/index.md @@ -334,7 +334,7 @@ La DeFi è un movimento open source. I protocolli e le applicazioni della DeFi s Di più sulla creazione di dapp -## Ulteriori letture {#futher-reading} +## Letture consigliate {#further-reading} ### Dati sulla DeFi {#defi-data} diff --git a/public/content/translations/it/desci/index.md b/public/content/translations/it/desci/index.md index 884f23eeb52..3ea1475dae1 100644 --- a/public/content/translations/it/desci/index.md +++ b/public/content/translations/it/desci/index.md @@ -104,10 +104,11 @@ Esplora i progetti e unisciti alla community della DeSci. - [Bio.xyz: ricevi finanziamenti per la tua DAO biotecnologica o il tuo progetto di DeSci](https://www.bio.xyz/) - [Fleming Protocol: economia dei dati open source che alimenta la scoperta biomedica collaborativa](http://flemingprotocol.io/) - [Istituto di Inferenza Attiva](https://www.activeinference.org/) -- [CureDAO: piattaforma sanitaria di precisione, gestita dalla community](https://docs.curedao.org/) - [IdeaMarkets: la credibilità scientifica decentralizzata diventa realtà](https://ideamarket.io/) - [DeSci Labs](https://www.desci.com/) - [ValleyDAO: una community globale e aperta che offre finanziamenti e supporto traslazionale per la ricerca biologica sintetica](https://www.valleydao.bio) +- [Cerebrum DAO: soluzioni di approvvigionamento e cura per far progredire la salute cerebrale e prevenire la neurodegenerazione](https://www.cerebrumdao.com/) +- [CryoDAO: finanziamento della rivoluzionaria ricerca nel campo della crioconservazione](https://www.cryodao.org) Accogliamo suggerimenti per nuovi progetti da elencare: per iniziare, ti preghiamo di consultare la nostra [politica di inserzione](/contributing/adding-desci-projects/)! diff --git a/public/content/translations/it/staking/saas/index.md b/public/content/translations/it/staking/saas/index.md index 33f0703fdad..f0448f70ab2 100644 --- a/public/content/translations/it/staking/saas/index.md +++ b/public/content/translations/it/staking/saas/index.md @@ -39,7 +39,7 @@ Gli indicatori d'attributo sono usati di seguito per segnalare notevoli punti di ## Esplora i fornitori del servizio di staking {#saas-providers} -Di seguito alcuni fornitori di SaaS disponibili. Usa i suddetti indicatori per orientarti tra questi servizi +Seguono alcuni dei fornitori di SaaS disponibili. Usa i suddetti indicatori per orientarti tra questi servizi diff --git a/public/content/translations/it/staking/withdrawals/index.md b/public/content/translations/it/staking/withdrawals/index.md index b9863b2f77f..bc8c9a2fb28 100644 --- a/public/content/translations/it/staking/withdrawals/index.md +++ b/public/content/translations/it/staking/withdrawals/index.md @@ -196,7 +196,7 @@ eventName="read more"> Gli operatori del validatore dovrebbero visitare la pagina dei Prelievi del Launchpad di Staking, dove troveranno ulteriori dettagli su come preparare il proprio validatore ai prelievi, le tempistiche degli eventi e ulteriori dettagli sul funzionamento dei prelievi. -Per testare la tua configurazione su una rete di prova, visita prima il Launchpad di Staking della Rete di Prova di Goerli per iniziare. +Per testare la tua configurazione su una rete di prova, visita il Launchpad di Staking della rete di prova di Holesky per iniziare. diff --git a/public/content/translations/ja/contributing/index.md b/public/content/translations/ja/contributing/index.md index 2dd80c61c64..861294545c4 100644 --- a/public/content/translations/ja/contributing/index.md +++ b/public/content/translations/ja/contributing/index.md @@ -88,7 +88,7 @@ GitHubリポジトリには、オープンソースに初めて触れるデベ ## オンチェーン功績トークン(OAT)の申請 {#oat} -あなたのコントリビューションがethereum.orgにマージされると、[Galxe](https://galxe.com/ethereumorg/)で特別なバッジを請求できるように変更されます。 オンチェーン功績トークン(OAT)は、エコシステムをささやかながらより素晴らしいものにするのに貢献したことの証拠です。 +あなたの貢献がethereum.orgにマージされると、[Galxe](https://app.galxe.com/quest/ethereumorg)で特別なバッジを請求できるようになります。 オンチェーン功績トークン(OAT)は、エコシステムをささやかながらより素晴らしいものにするのに貢献したことの証拠です。 [OATの詳細](https://help.galxe.com/en/articles/7067290-galxe-oats-reward-and-celebrate-achievements) diff --git a/public/content/translations/ja/contributing/translation-program/how-to-translate/index.md b/public/content/translations/ja/contributing/translation-program/how-to-translate/index.md index 032f4f1ab07..0fad8bc00b6 100644 --- a/public/content/translations/ja/contributing/translation-program/how-to-translate/index.md +++ b/public/content/translations/ja/contributing/translation-program/how-to-translate/index.md @@ -70,7 +70,7 @@ Crowdinへログインすると、プロジェクトの説明と翻訳可能な また、上部のボタンで、既存の翻訳を検索できる「翻訳メモリ」や、重要な用語の説明や標準的な翻訳を収録した「用語集」に切り替えることができます。 -さらに詳しく知りたいですか? [Crowdinオンラインエディターの使用方法に関するドキュメント](https://support.crowdin.com/online-editor/)をご参照ください。 +詳細をご希望の場合は、 [Crowdinオンラインエディターの使用方法に関するドキュメント](https://support.crowdin.com/online-editor/)をご参照ください。 ### レビュープロセス {#review-process} diff --git a/public/content/translations/ja/decentralized-identity/index.md b/public/content/translations/ja/decentralized-identity/index.md index 8f8832c2cf5..ac1fcdceda4 100644 --- a/public/content/translations/ja/decentralized-identity/index.md +++ b/public/content/translations/ja/decentralized-identity/index.md @@ -73,7 +73,7 @@ summaryPoint3: 暗号技術により、今や再び自分自身のIDとアテス ### 4. シビル攻撃の防御 {#sybil-protection} -シビル攻撃とは、一人の人間が偽って複数の人間と思わせ、影響力を増大させることです。 [クアドラティック・ボーティング](/glossary/#quadratic-voting)を用いた助成金授与アプリケーションでは、より多くの投票が集まることで助成金の価値が上がり、多くのIDを使って寄付するというインセンティブが働くため、こうしたシビル攻撃を受けやすくなっています。 分散型アイデンティティは、多くの場合、特定の個人情報を明らかにすることなく、各参加者自身が機械ではなく本当に人間であることを証明するという負担を高めることで、シビル攻撃の防止に役立ちます。 +[クアドラティック・ボーティング](/glossary/#quadratic-voting)を用いた助成金授与アプリケーションでは、より多くの投票が集まることで助成金の価値が上がり、多くのIDを使って寄付するというインセンティブが働くため、[シビル攻撃](/glossary/#sybil-attack)を受けやすくなっています。 分散型アイデンティティは、多くの場合、特定の個人情報を明らかにすることなく、各参加者自身が機械ではなく本当に人間であることを証明するという負担を高めることで、シビル攻撃の防止に役立ちます。 ## アテステーションとは {#what-are-attestations} @@ -163,7 +163,7 @@ summaryPoint3: 暗号技術により、今や再び自分自身のIDとアテス - **[Proof of Humanity](https://www.proofofhumanity.id)** - _Proof of Humanity (PoH)は、イーサリアム上に構築されたソーシャルID認証システム_ - **[BrightID](https://www.brightid.org/)** - _ソーシャルグラフの作成と分析を通じて、本人確認の改革を目指す分散型オープンソース・ソーシャルアイデンティティネットワーク_ - **[walt.id](https://walt.id)** — _オープンソースの分散型アイデンティティとウォレットのインフラストラクチャです。これにより、デベロッパーや組織は、自己主権型アイデンティティとNFT/SBTを活用可能にします。_ -- **[Masca](https://masca.io/)** — _オープンソースの分散型アイデンティティ・ウォレットで、MetaMask Snapとして実装されています。これにより、ユーザーとデベロッパーは、分散型IDとVCを活用できます。_ +- **[Veramo](https://veramo.io/)** - _JavaScriptフレームワークで、暗号学的に検証可能なデータを誰でも簡単にアプリケーションで利用することができます。_ ## 参考文献 {#further-reading} diff --git a/public/content/translations/ja/defi/index.md b/public/content/translations/ja/defi/index.md index 74bd4bf0257..cccf7f0c373 100644 --- a/public/content/translations/ja/defi/index.md +++ b/public/content/translations/ja/defi/index.md @@ -334,7 +334,7 @@ PoolTogetherのような損失のない宝くじは、楽しく革新的な新 分散型アプリ(Dapp)構築の詳細 -## 参考文献 {#futher-reading} +## 参考文献 {#further-reading} ### 分散型金融(DeFi)データ {#defi-data} diff --git a/public/content/translations/ja/desci/index.md b/public/content/translations/ja/desci/index.md index 6c54dc026f7..bab267d7286 100644 --- a/public/content/translations/ja/desci/index.md +++ b/public/content/translations/ja/desci/index.md @@ -104,10 +104,11 @@ Web3の様式を活用することで科学データへのアクセスが大幅 - [Bio.xyz: バイオテクノロジーDAOや分散型サイエンスプロジェクトのための資金獲得](https://www.bio.xyz/) - [Flemingプロトコル: 共同のバイオメディカルの発見を促進するオープンソースのデータエコノミー](http://flemingprotocol.io/) - [Active Inference Institute](https://www.activeinference.org/) -- [CureDAO: コミュニティ所有のプレシジョンヘルスプラットフォーム](https://docs.curedao.org/) - [IdeaMarkets: 分散型科学の信ぴょう性の有効化](https://ideamarket.io/) - [DeSci Labs](https://www.desci.com/) - [ValleyDAO: オープンなグローバルコミュニティで合成生物学研究のファンディングおよび翻訳に関するサポートを提供](https://www.valleydao.bio) +- [Cerebrum DAO: 脳の健康と神経変性を防ぐためのソリューションの調達と育成](https://www.cerebrumdao.com/) +- [CryoDAO: 冷凍保存分野におけるムーンショット型研究へのファンディング](https://www.cryodao.org) 新しいプロジェクトを提案する際には[ポリシー一覧](/contributing/adding-desci-projects/)をご覧ください。 diff --git a/public/content/translations/ja/staking/withdrawals/index.md b/public/content/translations/ja/staking/withdrawals/index.md index 78c402d7f7f..19d4674700e 100644 --- a/public/content/translations/ja/staking/withdrawals/index.md +++ b/public/content/translations/ja/staking/withdrawals/index.md @@ -196,7 +196,7 @@ eventName="read more"> バリデータのオペレータは、「Staking Launchpad Withdrawals」のページにアクセスすることをお勧めします。このページでは、引き出しのためにバリデータが準備することについて詳細、イベントのタイミング、引き出しの機能に関する詳細が記載されています。 -最初にテストネットでのセットアップを試すには、Holesky Testnet Staking Launchpadを見て始めましょう。 +最初にテストネットでのセットアップを試すには、 Holesky Testnet Staking Launchpadを見て始めましょう。 diff --git a/public/content/translations/ru/defi/index.md b/public/content/translations/ru/defi/index.md index d2d35122e5f..704424f7f2c 100644 --- a/public/content/translations/ru/defi/index.md +++ b/public/content/translations/ru/defi/index.md @@ -53,7 +53,7 @@ DeFi — это обобщенный термин для финансовых п ## Все началось с Bitcoin... {#bitcoin} -Биткоин во многих отношениях был первым приложением DeFi. Биткоин позволяет вам действительно владеть валютой, контролировать ее и отправлять в любую точку мира. Это достигается за счет предоставления возможности большому числу людей, которые не доверяют друг другу, согласовать бухгалтерскую книгу без необходимости в доверенном посреднике. Биткоин открыт для всех, и никто не имеет полномочий изменять его правила. Правила Bitcoin, такие как его дефицит и открытость, прописаны в технологии. Это не похоже на традиционные финансы, где правительства могут печатать деньги, которые обесценивают ваши сбережения, а компании могут закрыть рынки. +Биткоин во многих отношениях был первым приложением DeFi. Биткоин позволяет вам действительно владеть валютой, контролировать ее и отправлять в любую точку мира. Это достигается за счет предоставления возможности большому числу людей, которые не доверяют друг другу, согласовывать реестр без необходимости в доверенном посреднике. Биткоин открыт для всех, и никто не имеет полномочий изменять его правила. Правила Bitcoin, такие как его дефицит и открытость, прописаны в технологии. Это не похоже на традиционные финансы, где правительства могут печатать деньги, которые обесценивают ваши сбережения, а компании могут закрыть рынки. Ethereum построен на этом. Как и в Bitcoin, правила не могут измениться, у всех есть доступ. Но здесь за счет [смарт-контрактов](/glossary/#smart-contract) эти цифровые деньги можно программировать, так что средства можно не только хранить и отправлять. @@ -137,7 +137,7 @@ Ethereum построен на этом. Как и в Bitcoin, правила н Это позволяет вам занимать деньги без проверки кредитоспособности или передачи личной информации. -#### Доступ к глобальным фондам {#access-global-funds} +#### Доступ к глобальным средствам {#access-global-funds} Когда вы используете децентрализованного кредитора, у вас есть доступ к средствам со всего мира, а не только к средствам, находящимся на хранении в выбранном вами банке или учреждении. Это делает кредиты более доступными и улучшает процентные ставки. @@ -334,7 +334,7 @@ DeFi — это движение с открытым исходным кодом Подробнее о децентрализованных приложениях -## Дополнительные ресурсы {#futher-reading} +## Дополнительная литература {#further-reading} ### Данные о DeFi {#defi-data} diff --git a/public/content/translations/zh-tw/contributing/index.md b/public/content/translations/zh-tw/contributing/index.md index 751d2743bff..eaf36252a59 100644 --- a/public/content/translations/zh-tw/contributing/index.md +++ b/public/content/translations/zh-tw/contributing/index.md @@ -88,7 +88,7 @@ Ethereum.org 是一個開源專案,擁有超過 **12000 名**貢獻者,幫 ## 領取你的鏈上成就代幣 (OAT) {#oat} -如果你的貢獻合併到 ethereum.org,你將要改到 [Galxe](https://galxe.com/ethereumorg/) 上領取特殊徽章。 鏈上成就代幣 (OAT) 證明你曾經協助生態系統變得更加出色。 +如果你的貢獻合併到 ethereum.org,你將有機會在 [Galxe](https://app.galxe.com/quest/ethereumorg) 上領取特殊徽章。 鏈上成就代幣 (OAT) 證明你曾經協助生態系統變得更加出色。 [有關鏈上成就代幣的更多資訊](https://help.galxe.com/en/articles/7067290-galxe-oats-reward-and-celebrate-achievements) diff --git a/public/content/translations/zh-tw/contributing/translation-program/how-to-translate/index.md b/public/content/translations/zh-tw/contributing/translation-program/how-to-translate/index.md index 63001e92bad..5fd69e162e5 100644 --- a/public/content/translations/zh-tw/contributing/translation-program/how-to-translate/index.md +++ b/public/content/translations/zh-tw/contributing/translation-program/how-to-translate/index.md @@ -70,7 +70,7 @@ description: 使用 Crowdin 翻譯 ethereum.org 的說明 透過使用頂部的按鈕,你還可以切換到翻譯記憶。在那裡,你可以搜尋現有的翻譯,或者切換到「詞彙表」,其中包含關鍵術語的描述和標準翻譯。 -想要學習更多功能嗎? 請隨時查看[有關使用 Crowdin 線上編輯器的文件](https://support.crowdin.com/online-editor/) +想了解更多嗎? 請隨時查看[有關使用 Crowdin 線上編輯器的文件](https://support.crowdin.com/online-editor/) ### 審核過程 {#review-process} diff --git a/public/content/translations/zh-tw/decentralized-identity/index.md b/public/content/translations/zh-tw/decentralized-identity/index.md index 0e54d7624be..08668d578f9 100644 --- a/public/content/translations/zh-tw/decentralized-identity/index.md +++ b/public/content/translations/zh-tw/decentralized-identity/index.md @@ -73,7 +73,7 @@ summaryPoint3: 多虧了加密技術,使用者現在擁有了再次發行、 ### 4 反女巫保護 {#sybil-protection} -女巫攻擊是指個人欺騙系統使某個系統認為他們是多個人而增加他們的影響力。 使用[平方投票法](/glossary/#quadratic-voting)的捐款應用程式很容易受到女巫攻擊,因為當更多人投票支持時,捐款的價值就會增加,從而激勵使用者將他們的貢獻分配給多個身分。 去中心化身分透過增加每位參與者的負擔來證明他們是真正的人,這有助於防止這種情況發生,而且通常也不必透露具體的私人資訊。 +使用[平方投票法](/glossary/#quadratic-voting)的捐款應用程式很容易受到[女巫攻擊](/glossary/#sybil-attack),因為當更多人投票支持時,捐款的價值就會增加,這會激勵使用者將他們的貢獻分配給多個身分。 去中心化身分透過增加每位參與者的負擔來證明他們是真正的人,這有助於防止這種情況發生,而且通常也不必透露具體的私人資訊。 ## 什麼是身分證明 {#what-are-attestations} @@ -163,7 +163,7 @@ summaryPoint3: 多虧了加密技術,使用者現在擁有了再次發行、 - **[人性證明](https://www.proofofhumanity.id)** - _人性證明 (PoH) 是建立在以太坊上的社交身分驗證系統。_ - **[BrightID](https://www.brightid.org/)** - _一個去中心化的開源社交身分網路,旨在通過創建和分析社交圖譜來改革身分驗證。_ - **[walt.id](https://walt.id)** — _一種使開發者和組織能夠利用自主權身份、非同質化代幣/魂縛代幣的開源去中心化身份及錢包基礎設施。_ -- **[Masca](https://masca.io/)** — _開放原始碼的去中心化身分錢包,實作為 MetaMask Snap,使用者和開發者可以透過其使用去中心化身分識別 (DID) 和驗證者客戶端 (VC)。_ +- **[Veramo](https://veramo.io/)** - _ 一種 JavaScript 框架,讓任何人都可以輕鬆地在他們的應用程式中使用可加密驗證的資料。_ ## 延伸閱讀 {#further-reading} diff --git a/public/content/translations/zh-tw/defi/index.md b/public/content/translations/zh-tw/defi/index.md index 88239b0dab3..53f63389e32 100644 --- a/public/content/translations/zh-tw/defi/index.md +++ b/public/content/translations/zh-tw/defi/index.md @@ -334,7 +334,7 @@ Dai、USDC 等穩定幣的價值和美元的差距通常維持在幾美分之內 關於建構去中心化應用程式 -## 延伸閱讀 {#futher-reading} +## 了解更多 {#further-reading} ### 去中心化金融資料 {#defi-data} diff --git a/public/content/translations/zh-tw/desci/index.md b/public/content/translations/zh-tw/desci/index.md index b1317364b3d..457fc3b979c 100644 --- a/public/content/translations/zh-tw/desci/index.md +++ b/public/content/translations/zh-tw/desci/index.md @@ -104,10 +104,11 @@ Web3 廣泛試驗過去中心化自治組織和 Web3 開發的不同激勵模型 - [Bio.xyz:為你的生物技術去中心化自治組織或去中心化科研專案募資](https://www.bio.xyz/) - [Fleming Protocol:推動協作生物醫學發現的開源式資料經濟](http://flemingprotocol.io/) - [Active Inference Institute](https://www.activeinference.org/) -- [CureDAO:社群所有的精準健康平台](https://docs.curedao.org/) - [IdeaMarkets:實現去中心化的科學可信度](https://ideamarket.io/) - [去中心化科研實驗室](https://www.desci.com/) - [ValleyDAO:開放的全球社群,為合成生物學研究提供資金和轉譯支援](https://www.valleydao.bio) +- [Cerebrum DAO:尋找和培育解決方案以促進大腦健康並預防神經退化性疾病](https://www.cerebrumdao.com/) +- [CryoDAO:資助深低溫保存領域的「登月」研究](https://www.cryodao.org) 歡迎建議上架新專案 - 請由查看我們的[上架政策](/contributing/adding-desci-projects/)開始! diff --git a/public/content/translations/zh-tw/staking/withdrawals/index.md b/public/content/translations/zh-tw/staking/withdrawals/index.md index f2a2743b03a..9ce93763822 100644 --- a/public/content/translations/zh-tw/staking/withdrawals/index.md +++ b/public/content/translations/zh-tw/staking/withdrawals/index.md @@ -196,7 +196,7 @@ eventName="read more"> 建議驗證操作者訪問質押啟動面板提款頁面以便找到更多關於驗證者需要為提款作出的準備、活動時間,以及提款相關的詳細資訊。 -若想先在測試網上嘗試你的設置,請從造訪 Holesky 測試網質押啟動面板開始。 +若想先在測試網上測試你的設定,請造訪 Holesky 測試網質押啟動面板開始測試。 diff --git a/public/images/0xparc-logo.svg b/public/images/0xparc-logo.svg new file mode 100644 index 00000000000..dcb9b050a1f --- /dev/null +++ b/public/images/0xparc-logo.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/public/images/attestant-logo.svg b/public/images/attestant-logo.svg new file mode 100644 index 00000000000..9a6ca3dcde6 --- /dev/null +++ b/public/images/attestant-logo.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/images/dev-tools/pointer.png b/public/images/dev-tools/pointer.png deleted file mode 100644 index 4df67f75dc9..00000000000 Binary files a/public/images/dev-tools/pointer.png and /dev/null differ diff --git a/public/images/events/event-placeholder.png b/public/images/events/event-placeholder.png index 4a15165452a..a94614605d5 100644 Binary files a/public/images/events/event-placeholder.png and b/public/images/events/event-placeholder.png differ diff --git a/public/images/man-and-dog-playing.png b/public/images/man-and-dog-playing.png new file mode 100644 index 00000000000..14e75d2156e Binary files /dev/null and b/public/images/man-and-dog-playing.png differ diff --git a/public/images/man-baby-woman.png b/public/images/man-baby-woman.png new file mode 100644 index 00000000000..92ce90dcd26 Binary files /dev/null and b/public/images/man-baby-woman.png differ diff --git a/public/images/panda-ops-banner.png b/public/images/panda-ops-banner.png new file mode 100644 index 00000000000..6f80160d92e Binary files /dev/null and b/public/images/panda-ops-banner.png differ diff --git a/public/images/robot-help-bar.png b/public/images/robot-help-bar.png new file mode 100644 index 00000000000..4e698736a19 Binary files /dev/null and b/public/images/robot-help-bar.png differ diff --git a/public/images/solidity-banner.png b/public/images/solidity-banner.png new file mode 100644 index 00000000000..3fd5a37c62e Binary files /dev/null and b/public/images/solidity-banner.png differ diff --git a/public/images/upgrades/solidity.png b/public/images/upgrades/solidity.png new file mode 100644 index 00000000000..c1e3de9e7c4 Binary files /dev/null and b/public/images/upgrades/solidity.png differ diff --git a/public/images/upgrades/vyper.png b/public/images/upgrades/vyper.png new file mode 100644 index 00000000000..2d93c99088b Binary files /dev/null and b/public/images/upgrades/vyper.png differ diff --git a/public/images/vitalik-blog-banner.svg b/public/images/vitalik-blog-banner.svg new file mode 100644 index 00000000000..cea992081c9 --- /dev/null +++ b/public/images/vitalik-blog-banner.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/images/wallets/1inch.png b/public/images/wallets/1inch.png index 971ff3a3259..6019b8b1e35 100644 Binary files a/public/images/wallets/1inch.png and b/public/images/wallets/1inch.png differ diff --git a/public/images/wallets/imtoken.png b/public/images/wallets/imtoken.png index 5f86e3dec97..85db610c788 100644 Binary files a/public/images/wallets/imtoken.png and b/public/images/wallets/imtoken.png differ diff --git a/public/images/wallets/infinity_wallet.png b/public/images/wallets/infinity_wallet.png index 574e5f20be7..5cebdf8744d 100644 Binary files a/public/images/wallets/infinity_wallet.png and b/public/images/wallets/infinity_wallet.png differ diff --git a/src/@chakra-ui/components/Button.ts b/src/@chakra-ui/components/Button.ts index 1b51d328012..c0741641a70 100644 --- a/src/@chakra-ui/components/Button.ts +++ b/src/@chakra-ui/components/Button.ts @@ -44,16 +44,16 @@ const baseStyle = defineStyle({ }) const variantSolid = defineStyle({ - color: "background.base", - bg: "primary.base", + color: "white", + bg: "primary.action", borderColor: "transparent", _disabled: { bg: "disabled", color: "background.base", }, _hover: { - color: "background.base", - bg: "primary.hover", + color: "white", + bg: "primary.actionHover", boxShadow: "buttonHover", }, _active: { diff --git a/src/@chakra-ui/foundations/colors.ts b/src/@chakra-ui/foundations/colors.ts index dbf3e6cd57e..58f443a9282 100644 --- a/src/@chakra-ui/foundations/colors.ts +++ b/src/@chakra-ui/foundations/colors.ts @@ -2,41 +2,54 @@ export type Colors = typeof colors const colors = { gray: { - 100: "#F7F7F7", - 150: "#F2F2F2", - 200: "#E7E7E7", - 300: "#C8C8C8", + 50: "#f7f7f7", + 100: "#eeeeee", + 150: "#ececec", + 200: "#cecece", + 300: "#acacac", 400: "#8C8C8C", 500: "#616161", 600: "#333333", 700: "#222222", - 800: "#1B1B1B", - 900: "#141414", + 800: "#1b1b1b", + 900: "#121212", + 950: "#0a0a0a", }, blue: { - 50: "#F6F6FF", - 100: "#EBEBFF", - 200: "#D6D6FF", - 300: "#9999FF", - 400: "#5555FF", - 500: "#1C1CFF", - 600: "#0000E0", - 700: "#0000A3", - 800: "#000066", - 900: "#000029", + 50: "#F8FBFF", + 100: "#E8F1FF", + 200: "#CADFFB", + 300: "#88AAF1", + 400: "#6995F7", + 500: "#4473EF", + 600: "#3C4CEB", + 700: "#2B36A8", + 800: "#232F71", + 900: "#1B273A", }, orange: { 50: "#FFF3ED", - 100: "#FFE5D6", - 200: "#FFCBAD", - 300: "#FFB185", - 400: "#FF985C", - 500: "#FF7324", - 550: "#DF5A0E", - 600: "#B84300", - 700: "#7A2D00", - 800: "#521E00", - 900: "#2F1000", + 100: "#FFF0DB", + 200: "#FFD7A7", + 300: "#FEB077", + 400: "#FD8640", + 500: "#FB610E", + 600: "#EC4A0A", + 700: "#C4350A", + 800: "#7D2711", + 900: "#3A291D", + }, + purple: { + 50: "#F3ECFF", + 100: "#EDE2FF", + 200: "#DAC5FC", + 300: "#CCAFFC", + 400: "#B38DF0", + 500: "#945AF4", + 600: "#6C24DF", + 700: "#561BB5", + 800: "#41128B", + 900: "#1E0546", }, red: { 100: "#f7c8c8", diff --git a/src/@chakra-ui/semanticTokens.ts b/src/@chakra-ui/semanticTokens.ts index 9b81ae3c21a..7d3a68a0a18 100644 --- a/src/@chakra-ui/semanticTokens.ts +++ b/src/@chakra-ui/semanticTokens.ts @@ -37,11 +37,13 @@ const semanticTokens = { // Main Set primary: { - base: { _light: "blue.500", _dark: "orange.500" }, - highContrast: { _light: "blue.800", _dark: "orange.100" }, - lowContrast: { _light: "blue.100", _dark: "orange.800" }, - hover: { _light: "blue.400", _dark: "orange.400" }, - visited: { _light: "blue.700", _dark: "orange.550" }, + base: { _light: "purple.600", _dark: "purple.400" }, + highContrast: { _light: "purple.800", _dark: "purple.200" }, + lowContrast: { _light: "purple.100", _dark: "purple.900" }, + hover: { _light: "purple.500", _dark: "purple.500" }, + visited: { _light: "purple.700", _dark: "purple.300" }, + action: { _light: "purple.600", _dark: "purple.600" }, + actionHover: { _light: "purple.500", _dark: "purple.500" }, // ! Deprecating primary.light light: { _light: "blue.100", _dark: "orange.100" }, // ! Deprecating primary.dark @@ -57,7 +59,7 @@ const semanticTokens = { inverted: { _light: "gray.100", _dark: "gray.800" }, }, background: { - base: { _light: "white", _dark: "gray.800" }, + base: { _light: "white", _dark: "gray.950" }, highlight: { _light: "gray.100", _dark: "gray.900" }, }, disabled: { _light: "gray.400", _dark: "gray.500" }, @@ -91,7 +93,6 @@ const semanticTokens = { _light: "blackAlpha.400", _dark: "whiteAlpha.400", }, - switchBackground: { _light: "gray.300", _dark: "whiteAlpha.400" }, hubHeroContentBg: { _light: "rgba(255, 255, 255, 0.80)", _dark: "rgba(34, 34, 34, 0.80)", diff --git a/src/@chakra-ui/theme.ts b/src/@chakra-ui/theme.ts index ccce5e63610..ddafd133eee 100644 --- a/src/@chakra-ui/theme.ts +++ b/src/@chakra-ui/theme.ts @@ -7,7 +7,15 @@ import semanticTokens from "./semanticTokens" const config: ThemeConfig = { cssVarPrefix: "eth", initialColorMode: "system", - useSystemColorMode: true, + /** + * Disable Chakra's system color subscription, as it works differently from + * `next-themes` and causes a desync with it. + * + * Chakra will always change the color mode based on the system preference. + * While `next-themes` will only change to the system preference if the user + * has `system` as their active theme. + */ + useSystemColorMode: false, } /** diff --git a/src/components/AssetDownload/index.tsx b/src/components/AssetDownload/index.tsx index f3a4e2e0c1d..613d8d9eceb 100644 --- a/src/components/AssetDownload/index.tsx +++ b/src/components/AssetDownload/index.tsx @@ -26,6 +26,7 @@ const AssetDownload = ({ artistName, artistUrl, image, + svgUrl, title, ...props }: AssetDownloadProps) => { @@ -66,12 +67,11 @@ const AssetDownload = ({ {t("page-assets-download-download")} ( {extname(imgSrc).slice(1).toUpperCase()}) - {/* Disables SVG due to bug: https://github.com/ethereum/ethereum-org-website/issues/12267 */} - {/* {svgUrl && ( + {svgUrl && ( {t("page-assets-download-download")} (SVG) - )} */} + )} ) diff --git a/src/components/BigNumber/index.tsx b/src/components/BigNumber/index.tsx new file mode 100644 index 00000000000..d276611edd4 --- /dev/null +++ b/src/components/BigNumber/index.tsx @@ -0,0 +1,76 @@ +import { type ReactNode } from "react" +import { useRouter } from "next/router" +import { useTranslation } from "next-i18next" +import { MdInfoOutline } from "react-icons/md" + +import { cn } from "@/lib/utils/cn" +import { isValidDate } from "@/lib/utils/date" + +import Tooltip from "../Tooltip" +import Link from "../ui/Link" + +type BigNumberProps = { + children: ReactNode + value?: ReactNode + sourceName?: string + sourceUrl?: string + lastUpdated?: number | string + className?: string +} + +const BigNumber = ({ + children, + value, + sourceName, + sourceUrl, + lastUpdated, + className, +}: BigNumberProps) => { + const { t } = useTranslation("common") + const { locale } = useRouter() + const lastUpdatedDisplay = + lastUpdated && isValidDate(lastUpdated) + ? new Intl.DateTimeFormat(locale, { + dateStyle: "medium", + }).format(new Date(lastUpdated)) + : "" + return ( +
+ {value ? ( + <> +
{value}
+
+ {children} + {sourceName && sourceUrl && ( + +

+ {t("data-provided-by")}{" "} + {sourceName} +

+ {lastUpdated && ( +

+ {t("last-updated")}: {lastUpdatedDisplay} +

+ )} + + } + > + +
+ )} +
+ + ) : ( + {t("loading-error-refresh")} + )} +
+ ) +} +export default BigNumber diff --git a/src/components/Buttons/SvgButtonLink.tsx b/src/components/Buttons/SvgButtonLink.tsx new file mode 100644 index 00000000000..a070a9e30dc --- /dev/null +++ b/src/components/Buttons/SvgButtonLink.tsx @@ -0,0 +1,73 @@ +import { cva, VariantProps } from "class-variance-authority" +import type { FC, ReactNode, SVGProps } from "react" + +import { cn } from "@/lib/utils/cn" + +import { BaseLink } from "../ui/Link" + +type SvgButtonLinkProps = { + Svg: FC> + label?: string + children: ReactNode + href: string + className?: string + size?: number +} + +const variants = cva("flex items-center gap-3.5", { + variants: { + variant: { + col: "flex-col text-center [&_.body]:text-center", + row: "flex-row text-start [&_.body]:text-start [&_.header]:self-start", + }, + }, + defaultVariants: { + variant: "row", + }, +}) + +type Variants = VariantProps + +const SvgButtonLink = ({ + label, + children, + Svg, + className, + variant, + ...props +}: SvgButtonLinkProps & Variants) => ( + +
+
+ +
+
+ {label &&

{label}

} + {children} +
+
+
+) + +export default SvgButtonLink diff --git a/src/components/CallToContribute/CallToContribute.stories.tsx b/src/components/CallToContribute/CallToContribute.stories.tsx new file mode 100644 index 00000000000..6deca53f2d8 --- /dev/null +++ b/src/components/CallToContribute/CallToContribute.stories.tsx @@ -0,0 +1,57 @@ +import { Meta, StoryObj } from "@storybook/react" + +import { ChildOnlyProp } from "@/lib/types" + +import { langViewportModes } from "../../../.storybook/modes" + +import CallToContributeComponent from "." + +const meta = { + title: "Molecules / Navigation / CallToContribute", + component: CallToContributeComponent, + args: { + editPath: + "https://github.com/ethereum/ethereum-org-website/tree/dev/public/content/developers/docs/index.md", + }, + parameters: { + layout: "fullscreen", + chromatic: { + modes: { + ...langViewportModes, + }, + }, + }, + decorators: [ + (Story) => ( + + + + + + ), + ], +} satisfies Meta + +export default meta + +type Story = StoryObj + +const ContentContainer = (props: ChildOnlyProp) => { + return ( +
+ ) +} + +const Content = (props: ChildOnlyProp) => { + return ( +
+ ) +} + +export const CallToContribute: Story = {} diff --git a/src/components/CallToContribute.tsx b/src/components/CallToContribute/index.tsx similarity index 92% rename from src/components/CallToContribute.tsx rename to src/components/CallToContribute/index.tsx index 75a5152ba71..b79a85d43af 100644 --- a/src/components/CallToContribute.tsx +++ b/src/components/CallToContribute/index.tsx @@ -4,11 +4,11 @@ import { Flex, FlexProps, Icon } from "@chakra-ui/react" import { ChildOnlyProp } from "@/lib/types" -import { ButtonLink } from "./Buttons" -import InlineLink from "./Link" -import OldHeading from "./OldHeading" -import Text from "./OldText" -import Translation from "./Translation" +import { ButtonLink } from "@/components/Buttons" +import InlineLink from "@/components/Link" +import OldHeading from "@/components/OldHeading" +import Text from "@/components/OldText" +import Translation from "@/components/Translation" export type CallToContributeProps = { editPath: string diff --git a/src/components/CardList.tsx b/src/components/CardList.tsx index ad0e7c490b7..6fc297f1377 100644 --- a/src/components/CardList.tsx +++ b/src/components/CardList.tsx @@ -13,6 +13,7 @@ import { import { BaseLink } from "@/components/Link" +import { MatomoEventOptions, trackCustomEvent } from "@/lib/utils/matomo" import * as url from "@/lib/utils/url" import { useRtlFlip } from "@/hooks/useRtlFlip" @@ -102,12 +103,14 @@ export type CardListProps = BoxProps & { items: CardProps[] imageWidth?: number clickHandler?: (idx: string | number) => void + customEventOptions?: MatomoEventOptions } const CardList = ({ items, imageWidth, clickHandler = () => null, + customEventOptions, ...props }: CardListProps) => ( @@ -122,7 +125,10 @@ const CardList = ({ ) : ( clickHandler(idx)} + onClick={() => { + customEventOptions && trackCustomEvent(customEventOptions) + clickHandler(idx) + }} mb={4} {...listItem} /> diff --git a/src/components/Chevron/index.tsx b/src/components/Chevron/index.tsx new file mode 100644 index 00000000000..33116e5cdd9 --- /dev/null +++ b/src/components/Chevron/index.tsx @@ -0,0 +1,21 @@ +import { MdChevronLeft, MdChevronRight } from "react-icons/md" + +import { cn } from "@/lib/utils/cn" + +import { useRtlFlip } from "@/hooks/useRtlFlip" + +export const ChevronNext = ({ + className, + ...props +}: React.HTMLAttributes) => { + const { twFlipForRtl } = useRtlFlip() + return +} + +export const ChevronPrev = ({ + className, + ...props +}: React.HTMLAttributes) => { + const { twFlipForRtl } = useRtlFlip() + return +} diff --git a/src/components/Codeblock.tsx b/src/components/Codeblock.tsx index 9f00303918c..ead9c3ddb85 100644 --- a/src/components/Codeblock.tsx +++ b/src/components/Codeblock.tsx @@ -6,34 +6,32 @@ import Highlight, { PrismTheme, } from "prism-react-renderer" import Prism from "prism-react-renderer/prism" -import { Box, BoxProps, Flex, useColorModeValue } from "@chakra-ui/react" // https://github.com/FormidableLabs/prism-react-renderer/tree/master#custom-language-support import CopyToClipboard from "@/components/CopyToClipboard" import Emoji from "@/components/Emoji" +import { Flex } from "@/components/ui/flex" + +import { cn } from "@/lib/utils/cn" import { LINES_BEFORE_COLLAPSABLE } from "@/lib/constants" + +import useColorModeValue from "@/hooks/useColorModeValue" ;(typeof global !== "undefined" ? global : window).Prism = Prism require("prismjs/components/prism-solidity") -const TopBarItem = (props: BoxProps) => { - const bgColor = useColorModeValue("#f7f7f7", "#363641") - +const TopBarItem = ({ + className, + ...props +}: React.HTMLAttributes) => { return ( - ) @@ -42,8 +40,8 @@ const TopBarItem = (props: BoxProps) => { const codeTheme = { light: { plain: { - backgroundColor: "#fafafa", - color: "#333333", + backgroundColor: "#f7f7f7", // background-highlight (gray-50) + color: "#6C24DF", // primary (purple-600) }, styles: [ { @@ -114,8 +112,8 @@ const codeTheme = { dark: { // Pulled from `defaultProps.theme` for potential customization plain: { - backgroundColor: "#2a2734", - color: "#9a86fd", + backgroundColor: "#121212", // background-highlight (gray-900) + color: "#B38DF0", // primary (purple-400) }, styles: [ { @@ -251,18 +249,17 @@ const Codeblock = ({ return ( /* Overwrites codeblocks inheriting RTL styling in Right-To-Left script languages (e.g. Arabic) */ /* Context: https://github.com/ethereum/ethereum-org-website/issues/6202 */ - - +
{({ className, style, tokens, getLineProps, getTokenProps }) => ( - {tokens.map((line, i) => { return i === tokens.length - 1 && line[0].content === "" ? null : ( - {shouldShowLineNumbers && ( - + {i + 1} - + )} - + {line.map((token, key) => ( ))} - - + +
) })} {!fromHomepage && ( {allowCollapse && totalLines - 1 > LINES_BEFORE_COLLAPSABLE && ( @@ -349,11 +331,11 @@ const Codeblock = ({ )} )} -
+ )} -
-
+
+
) } diff --git a/src/components/CommunityEvents/index.tsx b/src/components/CommunityEvents/index.tsx deleted file mode 100644 index b963a8bb03d..00000000000 --- a/src/components/CommunityEvents/index.tsx +++ /dev/null @@ -1,201 +0,0 @@ -import { useRouter } from "next/router" -import { useTranslation } from "next-i18next" -import { FaDiscord } from "react-icons/fa" -import { - Box, - Center, - Divider, - Flex, - Grid, - GridItem, - Icon, -} from "@chakra-ui/react" - -import type { Lang } from "@/lib/types" -import type { CommunityEvent } from "@/lib/interfaces" - -import { ButtonLink } from "@/components/Buttons" -import InlineLink from "@/components/Link" -import OldHeading from "@/components/OldHeading" -import Text from "@/components/OldText" -import Translation from "@/components/Translation" - -import { trackCustomEvent } from "@/lib/utils/matomo" -import { getLocaleTimestamp } from "@/lib/utils/time" - -const matomoEvent = (buttonType: string) => { - trackCustomEvent({ - eventCategory: "CommunityEventsWidget", - eventAction: "clicked", - eventName: buttonType, - }) -} - -type EventProps = { - event: CommunityEvent - type: "upcoming" | "past" -} - -const Event = ({ event, type }: EventProps) => { - const { locale } = useRouter() - const { date, title, calendarLink } = event - const options: Intl.DateTimeFormatOptions = { - year: "numeric", - month: "short", - day: "numeric", - } - - return ( - - - - {getLocaleTimestamp(locale! as Lang, date, options)} - - - - matomoEvent(type)}> - {title} - - - - ) -} - -type CommunityEventsProps = { - events: { - pastEventData: CommunityEvent[] - upcomingEventData: CommunityEvent[] - } -} - -const CommunityEvents = ({ events }: CommunityEventsProps) => { - const { locale } = useRouter() - const { t } = useTranslation("page-index") - const { pastEventData, upcomingEventData } = events - - const reversedUpcomingEventData = upcomingEventData.slice().reverse() - const reversedPastEventData = pastEventData.slice().reverse() - - return ( - -
- - - {t("page-index:community-events-content-heading")} - - - - - {t("page-index:community-events-content-2")} - -
- - - - {reversedUpcomingEventData.length ? ( - - - {reversedUpcomingEventData[0].title} - - - {getLocaleTimestamp( - locale! as Lang, - reversedUpcomingEventData[0].date, - { - year: "numeric", - month: "long", - day: "numeric", - hour12: false, - hour: "numeric", - minute: "numeric", - } - )} - - - ({Intl.DateTimeFormat().resolvedOptions().timeZone}) - - - ) : ( - - {t("page-index:community-events-no-events-planned")} - - )} - - matomoEvent("discord")} - > - - Join Discord - - {reversedUpcomingEventData[0] && ( - matomoEvent("Add to calendar")} - fontWeight={700} - > - {t("community-events-add-to-calendar")} - - )} - - - - - - {t("page-index:community-events-upcoming-calls")} - - - {reversedUpcomingEventData.slice(1).length ? ( - reversedUpcomingEventData.slice(1).map((item, idx) => { - return - }) - ) : ( - - {t("page-index:community-events-no-upcoming-calls")} - - )} - - {t("page-index:community-events-previous-calls")} - - - {reversedPastEventData.length ? ( - reversedPastEventData.map((item, idx) => { - return - }) - ) : ( - - {t("page-index:community-events-there-are-no-past-calls")} - - )} - - -
- ) -} - -export default CommunityEvents diff --git a/src/components/EnergyConsumptionChart/index.tsx b/src/components/EnergyConsumptionChart/index.tsx index 43936c43655..6f8d0008657 100644 --- a/src/components/EnergyConsumptionChart/index.tsx +++ b/src/components/EnergyConsumptionChart/index.tsx @@ -212,9 +212,10 @@ const EnergyConsumptionChart = () => { font: { size: 10, }, - autoSkip: false, // avoid long labels to be hidden - padding: 0, // removes default padding betwen x-labels and chart - maxRotation: 0, // turns off rotation + autoSkip: false, + padding: 0, + maxRotation: 0, + minRotation: 45, }, }, }, diff --git a/src/components/EventCard.tsx b/src/components/EventCard.tsx index e4da86a39b6..fea404ed71d 100644 --- a/src/components/EventCard.tsx +++ b/src/components/EventCard.tsx @@ -1,13 +1,17 @@ import React from "react" import { useRouter } from "next/router" -import { useTranslation } from "react-i18next" +import { useTranslation } from "next-i18next" import { BsCalendar3 } from "react-icons/bs" import { Box, Flex, Heading, Icon } from "@chakra-ui/react" -import { Image } from "@chakra-ui/react" + +import type { EventCardProps } from "@/lib/types" import { ButtonLink } from "./Buttons" +import { TwImage } from "./Image" import Text from "./OldText" +import EventFallback from "@/public/images/events/event-placeholder.png" + const clearStyles = { content: '""', display: "block", @@ -15,18 +19,6 @@ const clearStyles = { clear: "both", } -export type EventCardProps = { - title: string - href: string - date: string - startDate: string - endDate: string - description: string - className?: string - location: string - imageUrl?: string -} - const EventCard = ({ title, href, @@ -90,14 +82,16 @@ const EventCard = ({ justifyContent="center" boxShadow="rgb(0 0 0 / 10%) 0px -1px 0px inset;" > - {title} + {imageUrl ? ( + // eslint-disable-next-line @next/next/no-img-element + {title} + ) : ( + + )} diff --git a/src/components/FindWallet/MobileFiltersMenu.tsx b/src/components/FindWallet/MobileFiltersMenu.tsx index 9abc938ee1f..73dbca3bbfb 100644 --- a/src/components/FindWallet/MobileFiltersMenu.tsx +++ b/src/components/FindWallet/MobileFiltersMenu.tsx @@ -26,8 +26,8 @@ import WalletFilterSidebar, { type MobileFiltersMenuProps = WalletFilterSidebarProps & { showMobileSidebar: boolean setFilters: React.Dispatch> - selectedPersona: number - setSelectedPersona: React.Dispatch> + selectedPersona: number[] + setSelectedPersona: React.Dispatch> onOpen: () => void onClose: () => void totalWallets: number @@ -89,6 +89,7 @@ export const MobileFiltersMenu = ({ selectedPersona={selectedPersona} setSelectedPersona={setSelectedPersona} showMobileSidebar={showMobileSidebar} + resetWalletFilter={resetWalletFilter} /> diff --git a/src/components/FindWallet/WalletFilterPersona.tsx b/src/components/FindWallet/WalletFilterPersona.tsx index eb4e57b4fad..cfb0d6d4c3c 100644 --- a/src/components/FindWallet/WalletFilterPersona.tsx +++ b/src/components/FindWallet/WalletFilterPersona.tsx @@ -1,4 +1,4 @@ -import { MdCircle } from "react-icons/md" +import { MdCheckBox } from "react-icons/md" import { Box, Flex, @@ -19,9 +19,32 @@ import { useWalletPersonas } from "../../hooks/useWalletPersonas" type WalletFilterPersonaProps = { resetFilters: () => void setFilters: React.Dispatch> - selectedPersona: number - setSelectedPersona: React.Dispatch> + selectedPersona: number[] + setSelectedPersona: React.Dispatch> showMobileSidebar: boolean + resetWalletFilter: React.MutableRefObject<() => void> +} + +const computeFilters = ( + selectedPersonas: number[], + personas: WalletPersonas[] +) => { + if (selectedPersonas.length === 0) return {} + + const firstPersona = personas[selectedPersonas[0]] + const initialFilters = firstPersona.presetFilters + return selectedPersonas + .slice(1) + .reduce((filters: { [key: string]: boolean }, personaId) => { + const persona = personas[personaId] + if (!persona) return filters + + return Object.fromEntries( + Object.entries(filters).map(([key, value]) => { + return [key, value || persona.presetFilters[key]] + }) + ) + }, initialFilters) } const WalletFilterPersona = ({ @@ -30,26 +53,50 @@ const WalletFilterPersona = ({ selectedPersona, setSelectedPersona, showMobileSidebar, + resetWalletFilter, }: WalletFilterPersonaProps) => { const personas = useWalletPersonas() const handleSelectPersona = (idx: number, persona: WalletPersonas) => { - if (idx === selectedPersona) { - resetFilters() - + let newSelectedPersonas + if (selectedPersona.includes(idx)) { + newSelectedPersonas = selectedPersona.filter((persona) => persona !== idx) trackCustomEvent({ eventCategory: "UserPersona", eventAction: `${persona.title}`, eventName: `${persona.title} false`, }) + setSelectedPersona(newSelectedPersonas) + if (newSelectedPersonas.length < 1) { + resetFilters() + resetWalletFilter.current() + } else { + const newFilters = computeFilters(newSelectedPersonas, personas) + setFilters((prevFilters) => { + const combinedFilters = Object.fromEntries( + Object.entries(prevFilters).map(([key]) => { + return [key, newFilters[key]] + }) + ) + return combinedFilters as WalletFilter + }) + } } else { - setSelectedPersona(idx) - setFilters(persona.presetFilters) - + newSelectedPersonas = [...selectedPersona, idx] trackCustomEvent({ eventCategory: "UserPersona", eventAction: `${persona.title}`, eventName: `${persona.title} true`, }) + setSelectedPersona(newSelectedPersonas) + const newFilters = computeFilters(newSelectedPersonas, personas) + setFilters((prevFilters) => { + const combinedFilters = Object.fromEntries( + Object.entries(prevFilters).map(([key, value]) => { + return [key, value || newFilters[key]] + }) + ) + return combinedFilters as WalletFilter + }) } } @@ -95,16 +142,19 @@ const WalletFilterPersona = ({ & { filters: WalletFilter resetWalletFilter: React.MutableRefObject<() => void> resetFilters: () => void + setFilters: React.Dispatch> + selectedPersona: number[] + setSelectedPersona: React.Dispatch> updateFilterOption: (key: unknown) => void updateFilterOptions: (keys: unknown, value: unknown) => void showMobileSidebar?: boolean diff --git a/src/components/Glossary/GlossaryTooltip/GlossaryTooltip.stories.tsx b/src/components/Glossary/GlossaryTooltip/GlossaryTooltip.stories.tsx index f203515d48e..7fe2ad9ea65 100644 --- a/src/components/Glossary/GlossaryTooltip/GlossaryTooltip.stories.tsx +++ b/src/components/Glossary/GlossaryTooltip/GlossaryTooltip.stories.tsx @@ -28,6 +28,6 @@ export const Basic: Story = {} // for chromatic story snapshot showing the rendered popover export const OnOpen: Story = { args: { - isOpen: true, + open: true, }, } diff --git a/src/components/Glossary/GlossaryTooltip/index.tsx b/src/components/Glossary/GlossaryTooltip/index.tsx index abaeec70ab0..a2ec9ed1dbf 100644 --- a/src/components/Glossary/GlossaryTooltip/index.tsx +++ b/src/components/Glossary/GlossaryTooltip/index.tsx @@ -1,8 +1,6 @@ import React, { ReactNode } from "react" import { useRouter } from "next/router" -import { Box, Text, VStack } from "@chakra-ui/react" -import Heading from "@/components/Heading" import InlineLink from "@/components/Link" import Tooltip, { type TooltipProps } from "@/components/Tooltip" import Translation from "@/components/Translation" @@ -23,12 +21,12 @@ const GlossaryTooltip = ({ const { asPath } = useRouter() return ( - + - +
+
- +
{/** * `as="span"` prevents hydration warnings for strings that contain * elements that cannot be nested inside `p` tags, like `ul` tags * (found in some Glossary definition). * TODO: Develop a better solution to handle this case. */} - + - - + +
} onBeforeOpen={() => { trackCustomEvent({ @@ -62,20 +60,11 @@ const GlossaryTooltip = ({ }) }} > - + {children} - +
-
+ ) } diff --git a/src/components/Hero/CallToAction.tsx b/src/components/Hero/CallToAction.tsx index e27110e354c..4f0d8e0baff 100644 --- a/src/components/Hero/CallToAction.tsx +++ b/src/components/Hero/CallToAction.tsx @@ -1,7 +1,8 @@ import type { ReactNode } from "react" -import { Button, type ButtonProps } from "@/components/Buttons" +import { Button, type ButtonProps } from "@/components/ui/buttons/Button" +import { cn } from "@/lib/utils/cn" import { type MatomoEventOptions, trackCustomEvent } from "@/lib/utils/matomo" export type CallToActionProps = Omit< @@ -17,6 +18,7 @@ export const CallToAction = ({ content, matomo, index, + className, ...props }: CallToActionProps) => { const handleClick = () => trackCustomEvent(matomo) @@ -24,7 +26,7 @@ export const CallToAction = ({ const buttonProps: ButtonProps = { variant: index === 0 ? "solid" : "outline", isSecondary: index !== 0, - flex: { base: 1, md: "initial" }, + className: cn("flex-[1] md:flex-[initial]", className), } return ( diff --git a/src/components/Hero/HomeHero/index.tsx b/src/components/Hero/HomeHero/index.tsx index c4408f80b5e..e333649c95a 100644 --- a/src/components/Hero/HomeHero/index.tsx +++ b/src/components/Hero/HomeHero/index.tsx @@ -1,51 +1,37 @@ import { useTranslation } from "next-i18next" -import { Box, Heading, Stack, Text, VStack } from "@chakra-ui/react" -import type { CommonHeroProps } from "@/lib/types" +import type { ClassNameProp, CommonHeroProps } from "@/lib/types" -import { ButtonLink } from "@/components/Buttons" -import { Image } from "@/components/Image" +import { TwImage } from "@/components/Image" import Morpher from "@/components/Morpher" -export type HomeHeroProps = Pick +export type HomeHeroProps = Pick & ClassNameProp -const HomeHero = ({ heroImg }: HomeHeroProps) => { +const HomeHero = ({ heroImg, className }: HomeHeroProps) => { const { t } = useTranslation("page-index") + return ( - - - +
+ - - - - - - - {t("page-index:page-index-title")} - - {t("page-index:page-index-description")} - - {t("page-index:page-index-title-button")} - - - - - +
+
+ +
+

{t("page-index:page-index-title")}

+

+ {t("page-index:page-index-description")} +

+
+
+ ) } diff --git a/src/components/Hero/HubHero/index.tsx b/src/components/Hero/HubHero/index.tsx index 09eea84930f..dd22ef7e0eb 100644 --- a/src/components/Hero/HubHero/index.tsx +++ b/src/components/Hero/HubHero/index.tsx @@ -1,9 +1,10 @@ -import { Box, Heading, Stack, Text } from "@chakra-ui/react" - import type { CommonHeroProps } from "@/lib/types" import { CallToAction } from "@/components/Hero/CallToAction" -import { Image } from "@/components/Image" +import { TwImage } from "@/components/Image" +import { Stack } from "@/components/ui/flex" + +import { cn } from "@/lib/utils/cn" export type HubHeroProps = Omit @@ -21,64 +22,48 @@ const HubHero = ({ } return ( - - + {title ? ( - +

{title} - +

) : null} - - - {header} - - {description} + + {title ? ( +

{header}

+ ) : ( +

{header}

+ )} + +

{description}

{buttons?.map((button, idx) => { if (!button) return @@ -86,7 +71,7 @@ const HubHero = ({ })}
-
+ ) } diff --git a/src/components/Homepage/BentoCard.tsx b/src/components/Homepage/BentoCard.tsx new file mode 100644 index 00000000000..1ff68bb53cb --- /dev/null +++ b/src/components/Homepage/BentoCard.tsx @@ -0,0 +1,53 @@ +import { HTMLAttributes } from "react" +import { type StaticImageData } from "next/image" + +import { TwImage } from "@/components/Image" +import { ButtonLink } from "@/components/ui/buttons/Button" + +import { cn } from "@/lib/utils/cn" + +import { ChevronNext } from "../Chevron" +import { Card, CardTitle } from "../ui/card" +import { Center } from "../ui/flex" + +export type BentoCardProps = HTMLAttributes & { + action: string + href: string + imgSrc: StaticImageData + imgWidth?: number + imgHeight?: number + title: string +} + +const BentoCard = ({ + action, + children, + className, + href, + imgSrc, + imgWidth, + imgHeight, + title, +}: BentoCardProps) => ( + +
+ +
+
+ + {title} + +

{children}

+ + {action} + +
+
+) + +export default BentoCard diff --git a/src/components/Homepage/useBentoBox.ts b/src/components/Homepage/useBentoBox.ts new file mode 100644 index 00000000000..ecfd63f7b34 --- /dev/null +++ b/src/components/Homepage/useBentoBox.ts @@ -0,0 +1,137 @@ +import { useTranslation } from "next-i18next" + +import { cn } from "@/lib/utils/cn" + +import ImpactImage from "@/public/images/impact_transparent.png" +import ManAndDogImage from "@/public/images/man-and-dog-playing.png" +import ManBabyWomanImage from "@/public/images/man-baby-woman.png" +import RobotBarImage from "@/public/images/robot-help-bar.png" +import MergeImage from "@/public/images/upgrades/merge.png" + +type Breakpoint = "mobile" | "lg" | "xl" +type Direction = "down" | "up" | "right" | "left" +type Color = "primary" | "accent-a" | "accent-b" | "accent-c" + +const gradientStops = "from-20% to-60%" + +const colorOptions: Record = { + primary: cn( + gradientStops, + "from-primary/10 to-primary/5 dark:from-primary/20 dark:to-primary/10 border-primary/10" + ), + "accent-a": cn( + gradientStops, + "from-accent-a/10 to-accent-a/5 dark:from-accent-a/20 dark:to-accent-a/10 border-accent-a/10" + ), + "accent-b": cn( + gradientStops, + "from-accent-b/10 to-accent-b/5 dark:from-accent-b/20 dark:to-accent-b/10 border-accent-b/10" + ), + "accent-c": cn( + gradientStops, + "from-accent-c/10 to-accent-c/5 dark:from-accent-c/20 dark:to-accent-c/10 border-accent-c/10" + ), +} + +const flow: Record> = { + mobile: { + down: "flex-col bg-gradient-to-b", + up: "flex-col-reverse bg-gradient-to-t", + right: "flex-row bg-gradient-to-r", + left: "flex-row-reverse bg-gradient-to-l", + }, + lg: { + down: "lg:flex-col lg:bg-gradient-to-b", + up: "lg:flex-col-reverse lg:bg-gradient-to-t", + right: "lg:flex-row lg:bg-gradient-to-r", + left: "lg:flex-row-reverse lg:bg-gradient-to-l", + }, + xl: { + down: "xl:flex-col xl:bg-gradient-to-b", + up: "xl:flex-col-reverse xl:bg-gradient-to-t", + right: "xl:flex-row xl:bg-gradient-to-r", + left: "xl:flex-row-reverse xl:bg-gradient-to-l", + }, +} + +const stylesByPosition: Record = { + mobile: [ + flow.mobile.down, + flow.mobile.down, + flow.mobile.down, + flow.mobile.down, + flow.mobile.down, + ], + lg: [ + cn("lg:col-span-6 lg:row-start-2", flow.lg.up), + cn("lg:col-span-6 lg:col-start-7 lg:row-start-2", flow.lg.down), + cn("lg:col-span-12 lg:row-start-3", flow.lg.right), + cn("lg:col-span-6 lg:col-start-7 lg:row-start-4", flow.lg.up), + 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), + ], +} + +const getPosition = (position: number): string => + cn( + stylesByPosition.mobile[position], + stylesByPosition.lg[position], + stylesByPosition.xl[position] + ) + +export const useBentoBox = () => { + const { t } = useTranslation("page-index") + + return [ + { + title: t("page-index-bento-stablecoins-title"), + children: t("page-index:page-index-bento-stablecoins-content"), + action: t("page-index:page-index-bento-stablecoins-action"), + href: "/stablecoins/", + imgSrc: ImpactImage, + imgWidth: 400, + className: cn(colorOptions["primary"], getPosition(0)), + }, + { + title: t("page-index:page-index-bento-defi-title"), + children: t("page-index:page-index-bento-defi-content"), + action: t("page-index:page-index-bento-defi-action"), + href: "/defi/", + imgSrc: ManAndDogImage, + className: cn(colorOptions["accent-c"], getPosition(1)), + }, + { + title: t("page-index:page-index-bento-dapps-title"), + children: t("page-index:page-index-bento-dapps-content"), + action: t("page-index:page-index-bento-dapps-action"), + href: "/dapps/", + imgSrc: MergeImage, + imgWidth: 320, + className: cn(colorOptions["accent-b"], getPosition(2)), + }, + { + title: t("page-index:page-index-bento-networks-title"), + children: t("page-index:page-index-bento-networks-content"), + action: t("page-index:page-index-bento-networks-action"), + href: "/layer-2/", + imgSrc: ManBabyWomanImage, + imgWidth: 324, + className: cn(colorOptions["accent-a"], getPosition(3)), + }, + { + title: t("page-index:page-index-bento-assets-title"), + children: t("page-index:page-index-bento-assets-content"), + action: t("page-index:page-index-bento-assets-action"), + href: "/nft/", + imgSrc: RobotBarImage, + imgWidth: 324, + className: cn(colorOptions["primary"], getPosition(4)), + }, + ] +} diff --git a/src/components/Homepage/useHome.ts b/src/components/Homepage/useHome.ts new file mode 100644 index 00000000000..1d7993aee5f --- /dev/null +++ b/src/components/Homepage/useHome.ts @@ -0,0 +1,203 @@ +import { useState } from "react" +import { useRouter } from "next/router" +import { useTranslation } from "next-i18next" +import { FaDiscord, FaGithub, FaXTwitter } from "react-icons/fa6" + +import type { EventCardProps, Lang } from "@/lib/types" +import type { CodeExample } from "@/lib/interfaces" + +import { useBentoBox } from "@/components/Homepage/useBentoBox" +import BlockHeap from "@/components/icons/block-heap.svg" +import EthGlyphIcon from "@/components/icons/eth-glyph.svg" +import EthTokenIcon from "@/components/icons/eth-token.svg" +import PickWalletIcon from "@/components/icons/eth-wallet.svg" +import ChooseNetworkIcon from "@/components/icons/network-layers.svg" +import TryAppsIcon from "@/components/icons/phone-homescreen.svg" +import RoadmapSign from "@/components/icons/roadmap-sign.svg" +import Whitepaper from "@/components/icons/whitepaper.svg" + +import { isValidDate } from "@/lib/utils/date" +import { isLangRightToLeft } from "@/lib/utils/translations" + +import events from "@/data/community-events.json" +import CreateWalletContent from "@/data/CreateWallet" + +import { GITHUB_REPO_URL } from "@/lib/constants" + +import SimpleDomainRegistryContent from "!!raw-loader!@/data/SimpleDomainRegistry.sol" +import SimpleTokenContent from "!!raw-loader!@/data/SimpleToken.sol" +import SimpleWalletContent from "!!raw-loader!@/data/SimpleWallet.sol" + +export const useHome = () => { + const { t } = useTranslation(["common", "page-index"]) + const { locale, asPath } = useRouter() + + const [isModalOpen, setModalOpen] = useState(false) + const [activeCode, setActiveCode] = useState(0) + + const bentoItems = useBentoBox() + + const dir = isLangRightToLeft(locale as Lang) ? "rtl" : "ltr" + + const toggleCodeExample = (id: number): void => { + setActiveCode(id) + setModalOpen(true) + } + + const codeExamples: CodeExample[] = [ + { + title: t("page-index:page-index-developers-code-example-title-0"), + description: t( + "page-index:page-index-developers-code-example-description-0" + ), + codeLanguage: "language-solidity", + code: SimpleWalletContent, + }, + { + title: t("page-index:page-index-developers-code-example-title-1"), + description: t( + "page-index:page-index-developers-code-example-description-1" + ), + codeLanguage: "language-solidity", + code: SimpleTokenContent, + }, + { + title: t("page-index:page-index-developers-code-example-title-2"), + description: t( + "page-index:page-index-developers-code-example-description-2" + ), + codeLanguage: "language-javascript", + code: CreateWalletContent, + }, + { + title: t("page-index:page-index-developers-code-example-title-3"), + description: t( + "page-index:page-index-developers-code-example-description-3" + ), + codeLanguage: "language-solidity", + code: SimpleDomainRegistryContent, + }, + ] + + const subHeroCTAs = [ + { + label: t("page-index:page-index-cta-wallet-label"), + description: t("page-index:page-index-cta-wallet-description"), + href: "/wallets/find-wallet/", + Svg: PickWalletIcon, + className: "text-primary hover:text-primary-hover", // TODO: Confirm hover style + }, + { + label: t("page-index:page-index-cta-get-eth-label"), + description: t("page-index:page-index-cta-get-eth-description"), + href: "/get-eth/", + Svg: EthTokenIcon, + className: "text-accent-a hover:text-accent-a-hover", + }, + { + label: t("page-index:page-index-cta-networks-label"), + description: t("page-index:page-index-cta-networks-description"), + href: "/layer-2/", // TODO: Update with new networks page when ready + Svg: ChooseNetworkIcon, + className: "text-accent-b hover:text-accent-b-hover", + }, + { + label: t("page-index:page-index-cta-dapps-label"), + description: t("page-index:page-index-cta-dapps-description"), + href: "/dapps/", + Svg: TryAppsIcon, + className: "text-accent-c hover:text-accent-c-hover", + }, + ] + + const popularTopics = [ + { + label: t("page-index:page-index-popular-topics-ethereum"), + Svg: EthTokenIcon, + href: "/what-is-ethereum/", + }, + { + label: t("page-index:page-index-popular-topics-wallets"), + Svg: PickWalletIcon, + href: "/wallets/", + }, + { + label: t("page-index:page-index-popular-topics-start"), + Svg: BlockHeap, + href: "/guides/", + }, + { + label: t("page-index:page-index-popular-topics-whitepaper"), + Svg: Whitepaper, + href: "/whitepaper/", + }, + { + label: t("page-index:page-index-popular-topics-roadmap"), + Svg: RoadmapSign, + href: "/roadmap/", + }, + ] + + const upcomingEvents = events + .filter((event) => { + const isValid = isValidDate(event.endDate) + const beginningOfEndDate = new Date(event.endDate).getTime() + const endOfEndDate = beginningOfEndDate + 24 * 60 * 60 * 1000 + const isUpcoming = endOfEndDate >= new Date().getTime() + return isValid && isUpcoming + }) + .sort( + (a, b) => new Date(a.endDate).getTime() - new Date(b.endDate).getTime() + ) + .slice(0, 3) as EventCardProps[] // Show 3 events ending soonest + + const joinActions = [ + { + Svg: EthGlyphIcon, + label: t("page-index:page-index-join-action-contribute-label"), + href: "/contributing/", + className: "text-accent-c hover:text-accent-c-hover", + description: t( + "page-index:page-index-join-action-contribute-description" + ), + }, + { + Svg: FaGithub, + label: "GitHub", + href: GITHUB_REPO_URL, + className: "text-accent-a hover:text-accent-a-hover", + description: t("page-index:page-index-join-action-github-description"), + }, + { + Svg: FaDiscord, + label: "Discord", + href: "/discord/", + className: "text-primary hover:text-primary-hover", + description: t("page-index:page-index-join-action-discord-description"), + }, + { + Svg: FaXTwitter, + label: "X", + href: "https://x.com/EthDotOrg", + className: "text-accent-b hover:text-accent-b-hover", + description: t("page-index:page-index-join-action-twitter-description"), + }, + ] + + return { + t, + locale, + asPath, + dir, + isModalOpen, + setModalOpen, + activeCode, + toggleCodeExample, + codeExamples, + subHeroCTAs, + popularTopics, + upcomingEvents, + joinActions, + bentoItems, + } +} diff --git a/src/components/LanguagePicker/index.tsx b/src/components/LanguagePicker/index.tsx index 0bcb653a176..b1c4b06e6df 100644 --- a/src/components/LanguagePicker/index.tsx +++ b/src/components/LanguagePicker/index.tsx @@ -1,5 +1,5 @@ import { useRouter } from "next/router" -import { useTranslation } from "react-i18next" +import { useTranslation } from "next-i18next" import { BaseLink } from "@/components/Link" diff --git a/src/components/Logo/Logo.stories.tsx b/src/components/Logo/Logo.stories.tsx new file mode 100644 index 00000000000..66a8be0858a --- /dev/null +++ b/src/components/Logo/Logo.stories.tsx @@ -0,0 +1,17 @@ +import * as React from "react" +import { Meta, StoryObj } from "@storybook/react" + +import LogoComponent from "." + +const meta = { + title: "Atoms / Media & Icons / Logo", + component: LogoComponent, +} satisfies Meta + +export default meta + +type Story = StoryObj + +export const Logo: Story = { + render: () => , +} diff --git a/src/components/Logo.tsx b/src/components/Logo/index.tsx similarity index 100% rename from src/components/Logo.tsx rename to src/components/Logo/index.tsx diff --git a/src/components/MainArticle.tsx b/src/components/MainArticle.tsx index 3ce72e677d4..cbe807d47a8 100644 --- a/src/components/MainArticle.tsx +++ b/src/components/MainArticle.tsx @@ -1,9 +1,16 @@ -import { Box, type BoxProps } from "@chakra-ui/react" +import { cn } from "@/lib/utils/cn" import { MAIN_CONTENT_ID } from "@/lib/constants" -const MainArticle = (props: BoxProps) => ( - +const MainArticle = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
) export default MainArticle diff --git a/src/components/Morpher.tsx b/src/components/Morpher.tsx index 1a084796890..0a8f7c91d87 100644 --- a/src/components/Morpher.tsx +++ b/src/components/Morpher.tsx @@ -1,6 +1,7 @@ import { useEffect, useState } from "react" +import { useMediaQuery } from "usehooks-ts" -import { Button } from "@/components/Buttons" +import { Button } from "@/components/ui/buttons/Button" import { DESKTOP_LANGUAGE_BUTTON_NAME, @@ -8,8 +9,6 @@ import { MOBILE_LANGUAGE_BUTTON_NAME, } from "@/lib/constants" -import { useBreakpointValue } from "@/hooks/useBreakpointValue" - const Morpher = () => { const [state, setState] = useState({ text: "Ethereum", @@ -126,6 +125,8 @@ const Morpher = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []) + const isLarge = useMediaQuery("(min-width: 48rem)") // TW md breakpoint, 768px + const handleMobileClick = () => { if (!document) return ;(document.getElementById(HAMBURGER_BUTTON_ID) as HTMLButtonElement).click() @@ -148,25 +149,16 @@ const Morpher = () => { ).click() } - const handleClick = - useBreakpointValue({ - base: handleMobileClick, - md: handleDesktopClick, - }) || handleDesktopClick - return ( - + <> + + ) } diff --git a/src/components/Nav/Desktop/index.tsx b/src/components/Nav/Desktop/index.tsx index b5cdf216510..92214ca540c 100644 --- a/src/components/Nav/Desktop/index.tsx +++ b/src/components/Nav/Desktop/index.tsx @@ -59,6 +59,11 @@ const DesktopNavMenu = ({ toggleColorMode }: DesktopNavMenuProps) => { variant="ghost" isSecondary px={{ base: "2", xl: "3" }} + sx={{ + "& > svg": { + transition: "transform 0.5s, color 0.2s", + }, + }} _hover={desktopHoverFocusStyles} _focus={desktopHoverFocusStyles} onClick={toggleColorMode} diff --git a/src/components/Nav/Menu/NextChevron.tsx b/src/components/Nav/Menu/NextChevron.tsx deleted file mode 100644 index 425d211b1bb..00000000000 --- a/src/components/Nav/Menu/NextChevron.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { useRouter } from "next/router" -import { MdChevronLeft, MdChevronRight } from "react-icons/md" -import { Icon, type IconProps } from "@chakra-ui/react" - -import type { Lang } from "@/lib/types" - -import { isLangRightToLeft } from "@/lib/utils/translations" - -const NextChevron = (props: IconProps) => { - const { locale } = useRouter() - const isRtl = isLangRightToLeft(locale! as Lang) - return -} - -export default NextChevron diff --git a/src/components/Nav/Menu/SubMenu.tsx b/src/components/Nav/Menu/SubMenu.tsx index e53f2a0f5ae..621772c304b 100644 --- a/src/components/Nav/Menu/SubMenu.tsx +++ b/src/components/Nav/Menu/SubMenu.tsx @@ -19,6 +19,7 @@ import { } from "@radix-ui/react-navigation-menu" import { ButtonProps } from "@/components/Buttons" +import { ChevronNext } from "@/components/Chevron" import Link from "@/components/Link" import { trackCustomEvent } from "@/lib/utils/matomo" @@ -27,7 +28,6 @@ import { cleanPath } from "@/lib/utils/url" import type { Level, NavItem, NavSectionKey } from "../types" import ItemContent from "./ItemContent" -import NextChevron from "./NextChevron" import { useSubMenu } from "./useSubMenu" type LvlContentProps = { @@ -83,7 +83,7 @@ const SubMenu = ({ lvl, items, activeSection, onClose }: LvlContentProps) => { const buttonProps: ButtonProps = { color: menuColors.body, leftIcon: lvl === 1 && icon ? : undefined, - rightIcon: isLink ? undefined : , + rightIcon: isLink ? undefined : , position: "relative", w: "full", me: -PADDING, diff --git a/src/components/Nav/useNav.ts b/src/components/Nav/useNav.ts index 32759b79e9c..0b3a2c20716 100644 --- a/src/components/Nav/useNav.ts +++ b/src/components/Nav/useNav.ts @@ -1,3 +1,4 @@ +import { useEffect } from "react" import { useTranslation } from "next-i18next" import { useTheme } from "next-themes" import { @@ -26,15 +27,11 @@ import { trackCustomEvent } from "@/lib/utils/matomo" import type { NavSections } from "./types" -import useColorModeValue from "@/hooks/useColorModeValue" - export const useNav = () => { const { t } = useTranslation("common") - const { resolvedTheme, setTheme } = useTheme() + const { setTheme, resolvedTheme, systemTheme } = useTheme() const { setColorMode } = useColorMode() - const colorToggleEvent = useColorModeValue("dark mode", "light mode") // This will be inverted as the state is changing - const linkSections: NavSections = { learn: { label: t("learn"), @@ -465,13 +462,27 @@ export const useNav = () => { }, } + // Listen for changes to systemTheme and update theme accordingly + // Important if the user has not engaged the color mode toggle yet, and + // toggles system color preferences + useEffect(() => { + setTheme("system") + setColorMode(systemTheme) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [systemTheme]) + const toggleColorMode = () => { - setTheme(resolvedTheme === "dark" ? "light" : "dark") - setColorMode(resolvedTheme === "dark" ? "light" : "dark") + // resolvedTheme: "light" | "dark" = Current resolved color mode from useTheme + const targetTheme = resolvedTheme === "dark" ? "light" : "dark" + // If target theme matches the users system pref, set ls theme to "system" + const lsTheme = targetTheme === systemTheme ? "system" : targetTheme + + setTheme(lsTheme) + setColorMode(targetTheme) trackCustomEvent({ eventCategory: "nav bar", eventAction: "click", - eventName: colorToggleEvent, + eventName: `${targetTheme} mode`, }) } diff --git a/src/components/PageHero.tsx b/src/components/PageHero.tsx index 16b749734a4..15029253ad5 100644 --- a/src/components/PageHero.tsx +++ b/src/components/PageHero.tsx @@ -1,16 +1,17 @@ import type { ReactNode } from "react" -import { Box, Center, Flex, Heading, Wrap, WrapItem } from "@chakra-ui/react" + +import { type ImageProps, TwImage } from "@/components/Image" + +import { cn } from "@/lib/utils/cn" +import { type MatomoEventOptions } from "@/lib/utils/matomo" import { Button, ButtonLink, - type ButtonLinkProps, - type ButtonProps, -} from "@/components/Buttons" -import { Image, type ImageProps } from "@/components/Image" -import Text from "@/components/OldText" - -import { type MatomoEventOptions, trackCustomEvent } from "@/lib/utils/matomo" + ButtonLinkProps, + ButtonProps, +} from "./ui/buttons/Button" +import { Center, Flex } from "./ui/flex" type ButtonLinkType = Omit & { content: ReactNode @@ -48,113 +49,89 @@ const PageHero = ({ children, className, }: PageHeroProps) => ( - +
- - +

{title} - - +

+ +

{header} - - - {subtitle} - +

+

{subtitle}

+ {buttons && ( - + {buttons.map((button, idx) => { const isSecondary = idx !== 0 if (isButtonLink(button)) { return ( - +
- trackCustomEvent({ - eventCategory: button.matomo.eventCategory, - eventAction: button.matomo.eventAction, - eventName: button.matomo.eventName, - }) - } + customEventOptions={{ + eventCategory: button.matomo.eventCategory, + eventAction: button.matomo.eventAction, + eventName: button.matomo.eventName, + }} isSecondary={isSecondary} > {button.content} - +
) } if (button.toId) { return ( - +
- +
) } })} -
+
)} {children} - +
-
-
+ ) export default PageHero diff --git a/src/components/Search/SearchButton.tsx b/src/components/Search/SearchButton.tsx index e8e9d1e79c1..19ae404f87c 100644 --- a/src/components/Search/SearchButton.tsx +++ b/src/components/Search/SearchButton.tsx @@ -1,5 +1,5 @@ import * as React from "react" -import { useTranslation } from "react-i18next" +import { useTranslation } from "next-i18next" import { DocSearchButton } from "@docsearch/react" import { Button, type ButtonProps } from "../ui/buttons/Button" diff --git a/src/components/Staking/StakingGuides.tsx b/src/components/Staking/StakingGuides.tsx index affb24f39ee..9b3a8357f2b 100644 --- a/src/components/Staking/StakingGuides.tsx +++ b/src/components/Staking/StakingGuides.tsx @@ -19,9 +19,14 @@ const StakingGuides = () => { }, { title: t("page-staking-guide-title-rocket-pool"), - link: "https://rocketpool.net/node-operators", + link: "https://docs.rocketpool.net/guides/node/responsibilities", description: t("page-staking-guide-description-mac-linux"), }, + { + title: t("page-staking-guide-title-stakewise"), + link: "https://docs.stakewise.io/guides/staking#liquid-solo-staking", + description: t("page-staking-guide-description-mac-linux-windows"), + }, ] return diff --git a/src/components/Staking/StakingHierarchy.tsx b/src/components/Staking/StakingHierarchy.tsx index ad980043d53..0a60718f47d 100644 --- a/src/components/Staking/StakingHierarchy.tsx +++ b/src/components/Staking/StakingHierarchy.tsx @@ -281,6 +281,7 @@ const StakingHierarchy = () => { {t("page-staking-hierarchy-solo-p2")} + {t("page-staking-hierarchy-solo-p3")} ( -
- {" "} - {metric.apiProvider} -
-) - -type GridItemProps = { - metric: StatsBoxMetric -} - -// ChartJS config -ChartJS.register( - CategoryScale, - LinearScale, - PointElement, - LineElement, - Filler, - // to avoid a production error, we must include this plugin even if we do - // not use it (we are using it on the energy consumption chart) - ChartDataLabels -) - -export const GridItem = ({ metric }: GridItemProps) => { - const { title, description, state, buttonContainer, range } = metric - const hasError = "error" in state - const hasData = "data" in state - - const value = hasError ? ( - - ) : ( - - - {state.value}{" "} - - - - - - - - - - ) - - // Returns either 90 or 30-day data range depending on `range` selection - const filteredData = (data: TimestampedData[]) => { - if (range === RANGES[1]) return [...data] - - return data.filter(({ timestamp }) => { - const millisecondRange = 1000 * 60 * 60 * 24 * 30 - const now = new Date().getTime() - - return timestamp >= now - millisecondRange - }) - } - - const minValue = hasData - ? state.data.reduce( - (prev, { value }) => (prev < value ? prev : value), - Infinity - ) - : 0 - - const maxValue = hasData - ? state.data.reduce((prev, { value }) => (prev > value ? prev : value), 0) - : 0 - - // ChartJS options - const chartOptions = { - // chart styles - borderColor: "#8884db", - borderWidth: 1, - tension: 0.3, - fill: true, - backgroundColor: (context: ScriptableContext<"line">) => { - const ctx = context.chart.ctx - const gradient = ctx.createLinearGradient(0, 0, 0, 220) - // gradient.addColorStop(offset, color) - gradient.addColorStop(0, "#8884d84d") - gradient.addColorStop(0.85, "#ffffff00") - - return gradient - }, - pointRadius: 0, - maintainAspectRatio: false, - // chart legend/title config - plugins: { - legend: { - display: false, // hide chart legend - }, - title: { - display: false, // hide titles - }, - // force disabling chart labels because when the user do an internal - // navigation, labels are displayed incorrectly (probably a bug in - // chart.js or the react wrapper) - datalabels: { - display: false, - }, - }, - // chart labels config - scales: { - y: { - display: false, // hide Y axis labels - grid: { - display: false, - }, - min: minValue, - max: maxValue, - }, - x: { - display: false, // hide X axis labels - grid: { - display: false, - }, - }, - }, - } - - const filteredRange = filteredData(hasData ? state.data : []) - - const chartData = { - labels: filteredRange, - datasets: [ - { - data: filteredRange.map((item) => item.value), - }, - ], - } - - return ( - - - - {title} - - {description} - - {hasData && ( - - - - )} - - - {value} - - {hasData && ( - - {buttonContainer} - - )} - - - ) -} diff --git a/src/components/StatsBoxGrid/RangeSelector.tsx b/src/components/StatsBoxGrid/RangeSelector.tsx deleted file mode 100644 index f20c2bd0a9b..00000000000 --- a/src/components/StatsBoxGrid/RangeSelector.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { Button } from "@chakra-ui/react" - -import { MatomoEventOptions, trackCustomEvent } from "@/lib/utils/matomo" - -import { RANGES } from "@/lib/constants" - -type RangeSelectorProps = { - state: string - setState: (state: string) => void - matomo: MatomoEventOptions -} - -export const RangeSelector = ({ - state, - setState, - matomo, -}: RangeSelectorProps) => ( -
- {RANGES.map((range, idx) => ( - - ))} -
-) diff --git a/src/components/StatsBoxGrid/index.tsx b/src/components/StatsBoxGrid/index.tsx index bda92a8f3a0..7b85bdd7372 100644 --- a/src/components/StatsBoxGrid/index.tsx +++ b/src/components/StatsBoxGrid/index.tsx @@ -1,31 +1,36 @@ -import { SimpleGrid } from "@chakra-ui/react" - import type { AllMetricData } from "@/lib/types" -import { GridItem } from "./GridItem" +import BigNumber from "../BigNumber" + import { useStatsBoxGrid } from "./useStatsBoxGrid" type StatsBoxGridProps = { - data: AllMetricData + metricResults: AllMetricData } +const StatsBoxGrid = ({ metricResults }: StatsBoxGridProps) => { + const metrics = useStatsBoxGrid(metricResults) -const StatsBoxGrid = ({ data }: StatsBoxGridProps) => { - const metrics = useStatsBoxGrid(data) - + const gridBorderClasses = [ + "border-b border-body-light xl:border-e xl:pe-8", + "border-b border-body-light xl:ps-8", + "border-b border-body-light xl:border-b-0 xl:border-e xl:pe-8", + "xl:ps-8", + ] return ( - - {metrics.map((metric, idx) => ( - +
+ {metrics.map(({ label, apiProvider, apiUrl, state }, idx) => ( + + {label} + ))} - +
) } diff --git a/src/components/StatsBoxGrid/useStatsBoxGrid.tsx b/src/components/StatsBoxGrid/useStatsBoxGrid.tsx index 1651b0a801a..afa150dc0ae 100644 --- a/src/components/StatsBoxGrid/useStatsBoxGrid.tsx +++ b/src/components/StatsBoxGrid/useStatsBoxGrid.tsx @@ -1,4 +1,7 @@ -import { useState } from "react" +/** + * TODO: Update metric for new homepage: + * - [ ] Replace TVL DeFi with "Total value held on Ethereum" + */ import { useRouter } from "next/router" import { useTranslation } from "next-i18next" @@ -6,170 +9,114 @@ import type { AllMetricData, Lang, StatsBoxMetric } from "@/lib/types" import { getLocaleForNumberFormat } from "@/lib/utils/translations" -import { RANGES } from "@/lib/constants" - -import { RangeSelector } from "./RangeSelector" - -const formatTotalStaked = (amount: number, locale: string): string => { +const formatLargeUSD = (value: number, locale: string): string => { return new Intl.NumberFormat(locale, { + style: "currency", + currency: "USD", notation: "compact", minimumSignificantDigits: 3, maximumSignificantDigits: 4, - }).format(amount) + }).format(value) } -const formatTVL = (tvl: number, locale: string): string => { +const formatSmallUSD = (value: number, locale: string): string => { return new Intl.NumberFormat(locale, { style: "currency", currency: "USD", notation: "compact", - minimumSignificantDigits: 3, - maximumSignificantDigits: 4, - }).format(tvl) + minimumSignificantDigits: 2, + maximumSignificantDigits: 3, + }).format(value) } -const formatTxs = (txs: number, locale: string): string => { +const formatLargeNumber = (value: number, locale: string): string => { return new Intl.NumberFormat(locale, { notation: "compact", minimumSignificantDigits: 3, maximumSignificantDigits: 4, - }).format(txs) -} - -const formatNodes = (nodes: number, locale: string): string => { - return new Intl.NumberFormat(locale, { - minimumSignificantDigits: 3, - maximumSignificantDigits: 4, - }).format(nodes) + }).format(value) } export const useStatsBoxGrid = ({ totalEthStaked, - nodeCount, totalValueLocked, txCount, + txCostsMedianUsd, + ethPrice, }: AllMetricData): StatsBoxMetric[] => { const { t } = useTranslation("page-index") const { locale } = useRouter() - const [selectedRangeTotalStaked, setSelectedRangeTotalStaked] = - useState(RANGES[0]) - const [selectedRangeTvl, setSelectedRangeTvl] = useState(RANGES[0]) - const [selectedRangeNodes, setSelectedRangeNodes] = useState( - RANGES[0] - ) - const [selectedRangeTxs, setSelectedRangeTxs] = useState(RANGES[0]) - const localeForNumberFormat = getLocaleForNumberFormat(locale! as Lang) - const totalEtherStaked = - "error" in totalEthStaked - ? { error: totalEthStaked.error } - : { - data: totalEthStaked.data, - value: formatTotalStaked(totalEthStaked.value, localeForNumberFormat), - } + const hasEthStakerAndPriceData = + "value" in totalEthStaked && "value" in ethPrice + const totalStakedInUsd = hasEthStakerAndPriceData + ? totalEthStaked.value * ethPrice.value + : 0 + + const totalEtherStaked = !totalStakedInUsd + ? { + error: + "error" in totalEthStaked + ? totalEthStaked.error + : "error" in ethPrice + ? ethPrice.error + : "", + } + : { + ...totalEthStaked, + value: formatLargeUSD(totalStakedInUsd, localeForNumberFormat), + } const valueLocked = "error" in totalValueLocked ? { error: totalValueLocked.error } : { - data: totalValueLocked.data, - value: formatTVL(totalValueLocked.value, localeForNumberFormat), + ...totalValueLocked, + value: formatLargeUSD(totalValueLocked.value, localeForNumberFormat), } const txs = "error" in txCount ? { error: txCount.error } : { - data: txCount.data, - value: formatTxs(txCount.value, localeForNumberFormat), + ...txCount, + value: formatLargeNumber(txCount.value, localeForNumberFormat), } - const nodes = - "error" in nodeCount - ? { error: nodeCount.error } + const medianTxCost = + "error" in txCostsMedianUsd + ? { error: txCostsMedianUsd.error } : { - data: nodeCount.data, - value: formatNodes(nodeCount.value, localeForNumberFormat), + ...txCostsMedianUsd, + value: formatSmallUSD(txCostsMedianUsd.value, localeForNumberFormat), } const metrics: StatsBoxMetric[] = [ { - apiProvider: "Dune Analytics", - apiUrl: "https://dune.com/", - title: t("page-index-network-stats-total-eth-staked"), - description: t("page-index-network-stats-total-eth-staked-explainer"), - buttonContainer: ( - - ), - state: totalEtherStaked, - range: selectedRangeTotalStaked, + apiProvider: "DeFi Llama", + apiUrl: "https://defillama.com/", + label: t("page-index-network-stats-value-defi-description"), + state: valueLocked, }, { - apiProvider: "Etherscan", - apiUrl: "https://etherscan.io/", - title: t("page-index-network-stats-tx-day-description"), - description: t("page-index-network-stats-tx-day-explainer"), - buttonContainer: ( - - ), + apiProvider: "GrowThePie", + apiUrl: "https://growthepie.xyz/", + label: t("page-index-network-stats-tx-day-description"), state: txs, - range: selectedRangeTxs, }, { - apiProvider: "DeFi Llama", - apiUrl: "https://defillama.com/", - title: t("page-index-network-stats-value-defi-description"), - description: t("page-index-network-stats-value-defi-explainer"), - buttonContainer: ( - - ), - state: valueLocked, - range: selectedRangeTvl, + apiProvider: "GrowThePie", + apiUrl: "https://growthepie.xyz/", + label: t("page-index-network-stats-tx-cost-description"), + state: medianTxCost, }, { - apiProvider: "Etherscan", - apiUrl: "https://etherscan.io/nodetracker", - title: t("page-index-network-stats-nodes-description"), - description: t("page-index-network-stats-nodes-explainer"), - buttonContainer: ( - - ), - state: nodes, - range: selectedRangeNodes, + apiProvider: "Dune Analytics", + apiUrl: "https://dune.com/", + label: t("page-index-network-stats-total-eth-staked"), + state: totalEtherStaked, }, ] diff --git a/src/components/Swiper/index.tsx b/src/components/Swiper/index.tsx new file mode 100644 index 00000000000..d235a381a03 --- /dev/null +++ b/src/components/Swiper/index.tsx @@ -0,0 +1,63 @@ +import { useTranslation } from "next-i18next" +import { EffectCards, Keyboard, Navigation, Pagination } from "swiper/modules" +import { Swiper as SwiperParent, SwiperSlide } from "swiper/react" +import type { SwiperOptions } from "swiper/types" + +import { ChevronNext, ChevronPrev } from "@/components/Chevron" + +import { cn } from "@/lib/utils/cn" + +import "swiper/css" +import "swiper/css/navigation" +import "swiper/css/pagination" +import "swiper/css/effect-cards" + +type SwiperProps = { + children: React.ReactNode[] + options?: SwiperOptions + className?: string + swiperClass?: string + sliderClass?: string +} +const Swiper = ({ + children, + className, + swiperClass, + sliderClass, + options, +}: SwiperProps) => { + const { t } = useTranslation("common") + return ( +
+ + {children.map((child, index) => ( + + {child} + + ))} + + +
+ + +
+ ) +} + +export default Swiper diff --git a/src/components/ThemeProvider.tsx b/src/components/ThemeProvider.tsx index d1595b91c04..c7cc096e453 100644 --- a/src/components/ThemeProvider.tsx +++ b/src/components/ThemeProvider.tsx @@ -27,7 +27,7 @@ const ThemeProvider = ({ children }: Pick) => { const theme = useMemo(() => merge(customTheme, { direction }), [direction]) return ( & { content: ReactNode children?: ReactNode onBeforeOpen?: () => void @@ -22,9 +22,10 @@ const Tooltip = ({ content, children, onBeforeOpen, - ...rest + ...props }: TooltipProps) => { const { isOpen, onOpen, onClose } = useDisclosure() + const isClient = useIsClient() // Close the popover when the user scrolls. // This is useful for mobile devices where the popover is open by clicking the @@ -57,24 +58,44 @@ const Tooltip = ({ onOpen() } + const handleOpenChange = (open: boolean) => { + if (open) { + handleOpen() + } else { + onClose() + } + } + + // Avoid rendering on the server since the user can't interact with it and we + // need to use different components depending on the device + if (!isClient) { + return null + } + + // Use Popover on mobile devices since the user can't hover + const Component = isMobile() ? Popover : Tooltipcomponent + const Trigger = isMobile() ? PopoverTrigger : TooltipTrigger + const Content = isMobile() ? PopoverContent : TooltipContent + return ( - - {children} - - - - {content} - - - + + {children} + + + {content} + + ) } diff --git a/src/components/Translation.tsx b/src/components/Translation.tsx index 626f26a7051..21cba5ed8af 100644 --- a/src/components/Translation.tsx +++ b/src/components/Translation.tsx @@ -13,12 +13,6 @@ type TranslationProps = { transform?: HtmrOptions["transform"] } -// Custom components mapping to be used by `htmr` when parsing the translation -// text -const defaultTransform = { - a: TooltipLink, -} - // Renders the translation string for the given translation key `id`. It // fallback to English if it doesn't find the given key in the current language const Translation = ({ id, options, transform = {} }: TranslationProps) => { @@ -28,6 +22,12 @@ const Translation = ({ id, options, transform = {} }: TranslationProps) => { const { t } = useTranslation(requiredNamespaces) const translatedText = t(id, options) + // Custom components mapping to be used by `htmr` when parsing the translation + // text + const defaultTransform = { + a: TooltipLink, + } + // Use `htmr` to parse html content in the translation text return htmr(translatedText, { transform: { ...defaultTransform, ...transform }, diff --git a/src/components/TranslationChartImage/TranslationChartImage.stories.tsx b/src/components/TranslationChartImage/TranslationChartImage.stories.tsx new file mode 100644 index 00000000000..f61a7e2f74b --- /dev/null +++ b/src/components/TranslationChartImage/TranslationChartImage.stories.tsx @@ -0,0 +1,24 @@ +import { Meta, StoryObj } from "@storybook/react" + +import TranslationChartImageComponent from "." + +const meta = { + title: "Atoms / Media & Icons / TranslationChartImage", + component: TranslationChartImageComponent, + parameters: { + layout: "fullscreen", + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], +} satisfies Meta + +export default meta + +type Story = StoryObj + +export const TranslationChartImage: Story = {} diff --git a/src/components/TranslationChartImage.tsx b/src/components/TranslationChartImage/index.tsx similarity index 75% rename from src/components/TranslationChartImage.tsx rename to src/components/TranslationChartImage/index.tsx index 51bf3acc6dc..f819899b6b8 100644 --- a/src/components/TranslationChartImage.tsx +++ b/src/components/TranslationChartImage/index.tsx @@ -1,6 +1,6 @@ import { useColorModeValue } from "@chakra-ui/react" -import { Image } from "@/components/Image" +import { TwImage } from "@/components/Image" import pageviewsDark from "@/public/images/translation-program/pageviews-dark.png" import pageviewsLight from "@/public/images/translation-program/pageviews-light.png" @@ -9,13 +9,10 @@ const TranslationChartImage = () => { const ethImage = useColorModeValue(pageviewsLight, pageviewsDark) return ( - ) } diff --git a/src/components/UpcomingEventsList.tsx b/src/components/UpcomingEventsList.tsx index 1bc062cc221..d03d3a2a74f 100644 --- a/src/components/UpcomingEventsList.tsx +++ b/src/components/UpcomingEventsList.tsx @@ -145,7 +145,6 @@ const UpcomingEventsList = () => { to, href, formattedDetails, - date, location, imageUrl, startDate, @@ -157,7 +156,6 @@ const UpcomingEventsList = () => { key={idx} title={title} href={to || href} - date={date} description={formattedDetails} location={location} imageUrl={imageUrl} diff --git a/src/components/WindowBox/index.tsx b/src/components/WindowBox/index.tsx new file mode 100644 index 00000000000..da52cb65960 --- /dev/null +++ b/src/components/WindowBox/index.tsx @@ -0,0 +1,29 @@ +import type { FC, ReactNode, SVGProps } from "react" + +import { cn } from "@/lib/utils/cn" + +type WindowBoxProps = { + title: ReactNode + Svg: FC> + children?: ReactNode + className?: string +} + +const WindowBox = ({ title, Svg, children, className }: WindowBoxProps) => ( +
+
+
+ +
+

{title}

+
+ {children} +
+) + +export default WindowBox diff --git a/src/components/icons/angle-brackets.svg b/src/components/icons/angle-brackets.svg new file mode 100644 index 00000000000..7dedd4d5cb6 --- /dev/null +++ b/src/components/icons/angle-brackets.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/components/icons/block-heap.svg b/src/components/icons/block-heap.svg new file mode 100644 index 00000000000..4319b7b9034 --- /dev/null +++ b/src/components/icons/block-heap.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/icons/calendar-add.svg b/src/components/icons/calendar-add.svg new file mode 100644 index 00000000000..89f48f3033d --- /dev/null +++ b/src/components/icons/calendar-add.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/components/icons/calendar.svg b/src/components/icons/calendar.svg new file mode 100644 index 00000000000..aa365893c54 --- /dev/null +++ b/src/components/icons/calendar.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/icons/eth-glyph.svg b/src/components/icons/eth-glyph.svg new file mode 100644 index 00000000000..0d15d4f95d4 --- /dev/null +++ b/src/components/icons/eth-glyph.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/components/icons/eth-token.svg b/src/components/icons/eth-token.svg new file mode 100644 index 00000000000..0dbbf26a378 --- /dev/null +++ b/src/components/icons/eth-token.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/icons/eth-wallet.svg b/src/components/icons/eth-wallet.svg new file mode 100644 index 00000000000..dfa521e49a2 --- /dev/null +++ b/src/components/icons/eth-wallet.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/icons/layer-2.svg b/src/components/icons/layer-2.svg new file mode 100644 index 00000000000..c30ee34895a --- /dev/null +++ b/src/components/icons/layer-2.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/components/icons/network-layers.svg b/src/components/icons/network-layers.svg new file mode 100644 index 00000000000..b78b04d247e --- /dev/null +++ b/src/components/icons/network-layers.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/icons/phone-homescreen.svg b/src/components/icons/phone-homescreen.svg new file mode 100644 index 00000000000..bc635f5e6d8 --- /dev/null +++ b/src/components/icons/phone-homescreen.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/components/icons/roadmap-sign.svg b/src/components/icons/roadmap-sign.svg new file mode 100644 index 00000000000..22114ed5704 --- /dev/null +++ b/src/components/icons/roadmap-sign.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/icons/whitepaper.svg b/src/components/icons/whitepaper.svg new file mode 100644 index 00000000000..0d96718efd1 --- /dev/null +++ b/src/components/icons/whitepaper.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/components/ui/buttons/Button.tsx b/src/components/ui/buttons/Button.tsx index 0f4dab69f40..fbf4e1278d8 100644 --- a/src/components/ui/buttons/Button.tsx +++ b/src/components/ui/buttons/Button.tsx @@ -14,14 +14,15 @@ const buttonVariants = cva( variants: { variant: { solid: - "text-background bg-primary border-transparent disabled:bg-disabled disabled:text-background hover:text-background hover:bg-primary-hover hover:shadow-button-hover active:shadow-none", - outline: "hover:shadow-button-hover active:shadow-none", + "text-white bg-primary-action border-transparent disabled:bg-disabled disabled:text-background hover:text-white hover:bg-primary-action-hover hover:shadow-button-hover active:shadow-none", + outline: "hover:shadow-button-hover active:shadow-none text-body", "outline-color": "hover:shadow-button-hover active:shadow-none border-primary", ghost: "border-transparent", link: "border-transparent font-bold underline py-0 px-1 active:text-primary", }, size: { + lg: "text-lg py-3 px-8 [&>svg]:text-2xl rounded-lg", md: "min-h-10.5 px-4 py-2 [&>svg]:text-2xl", sm: "text-xs min-h-[31px] py-1.5 px-2 [&>svg]:text-md", }, @@ -108,21 +109,40 @@ const Button = React.forwardRef( ) Button.displayName = "Button" -type ButtonLinkProps = Omit & { - buttonProps?: ButtonProps - customEventOptions?: MatomoEventOptions -} +type ButtonLinkProps = Omit & + Pick & { + buttonProps?: Omit + customEventOptions?: MatomoEventOptions + } const ButtonLink = React.forwardRef( - ({ buttonProps, customEventOptions, children, ...linkProps }, ref) => { + ( + { + size, + variant, + isSecondary, + buttonProps, + customEventOptions, + children, + className, + ...linkProps + }, + ref + ) => { const handleClick = () => { customEventOptions && trackCustomEvent(customEventOptions) } return ( - + ))} + +
+ + {isModalOpen && ( + // TODO: Migrate CodeModal, CodeBlock from Chakra-UI to tailwind/shad-cn + + }> + + {codeExamples[activeCode].code} + + + + )} + + + + {/* Ethereum.org community - Built by the community */} +
+ + + + + + {t("page-index:page-index-community-tag")} + + {t("page-index:page-index-community-header")} + +
+

{t("page-index:page-index-community-description-1")}

+

{t("page-index:page-index-community-description-2")}

+

{t("page-index:page-index-community-description-3")}

+
+
+ + {t("page-index:page-index-community-action")} - - - {/* Render CodeModal & Codeblock conditionally */} - {isModalOpen && ( - - }> - + - {codeExamples[activeCode].code} - - - + + + + + +
+ +
+ + {calendar.length > 0 ? ( + calendar.map(({ date, title, calendarLink }) => ( +
+
+ + {title} + +

+ {new Intl.DateTimeFormat(locale, { + month: "long", + day: "2-digit", + year: "numeric", + hour: "numeric", + minute: "numeric", + }).format(new Date(date))} +

+
+ + {" "} + {t("page-index:page-index-calendar-add")} + +
+ )) + ) : ( +
+ {t("page-index:page-index-calendar-fallback")} +
+ )} +
+
{" "} +
+
+ + {/* Recent posts */} +
+

+ {t("page-index:page-index-posts-header")} +

+

{t("page-index:page-index-posts-subtitle")}

+ + + {rssItems.map(({ pubDate, title, source, link, imgSrc }) => ( + + + {/* eslint-disable-next-line @next/next/no-img-element */} + + + + {title} + {isValidDate(pubDate) && ( + + {new Intl.DateTimeFormat(locale, { + month: "long", + day: "numeric", + year: "numeric", + }).format(new Date(pubDate))} + + )} + {source} + + + ))} + + +
+

{t("page-index:page-index-posts-action")}

+
+ {blogLinks.map(({ name, href }) => ( + + {name} + + ))} +
+
+
+ + {/* Events */} +
+

+ {t("page-index:page-index-events-header")} +

+

{t("page-index:page-index-events-subtitle")}

+
+
+ {upcomingEvents.map( + ( + { + title, + href, + location, + description, + startDate, + endDate, + imageUrl, + }, + idx + ) => ( + + + {imageUrl ? ( + // eslint-disable-next-line @next/next/no-img-element + + ) : ( + + )} + + + {title} + + {(isValidDate(startDate) || isValidDate(endDate)) && + new Intl.DateTimeFormat(locale, { + month: "long", + day: "numeric", + year: "numeric", + }).formatRange( + new Date( + isValidDate(startDate) ? startDate : endDate + ), + new Date(isValidDate(endDate) ? endDate : startDate) + )} + + {location} + + + ) + )} +
+
+
+ + {t("page-index:page-index-events-action")} + +
+
+ + {/* Join ethereum.org */} +
- - {/* Eth Today Section */} - - - - - - - - - - - } - componentProps={{ data: metricResults }} - intersectionOptions={{ - root: null, - rootMargin: "500px", - threshold: 0, - }} - /> - - - - {/* Explore Section */} - - - - - - - - {touts.map((tout, idx) => { - return ( - - ) - })} - - - - - - - } - variant="outline" - isSecondary - > - GitHub - - - - - +
+
+

{t("page-index:page-index-join-header")}

+

{t("page-index:page-index-join-description")}

+
+
+ {joinActions.map( + ({ Svg, label, href, className, description }) => ( + +

{description}

+
+ ) + )} +
+
+
+ + ) } diff --git a/src/pages/stablecoins.tsx b/src/pages/stablecoins.tsx index 2e93a0ab03a..d8c4fe89c39 100644 --- a/src/pages/stablecoins.tsx +++ b/src/pages/stablecoins.tsx @@ -394,7 +394,7 @@ const StablecoinsPage = ({ markets, marketsHasError }) => { { content: t("page-stablecoins-how-they-work-button"), toId: "how", - variant: "outline", + variant: "outline" as const, matomo: { eventCategory: "stablecoins hero buttons", eventAction: "click", diff --git a/src/pages/staking/index.tsx b/src/pages/staking/index.tsx index 05b18512967..5b8f9a99f92 100644 --- a/src/pages/staking/index.tsx +++ b/src/pages/staking/index.tsx @@ -422,6 +422,9 @@ const StakingPage = ({ {t("page-staking-section-comparison-solo-risks-li3")} + + {t("page-staking-section-comparison-solo-risks-li4")} +
diff --git a/src/pages/wallets/find-wallet.tsx b/src/pages/wallets/find-wallet.tsx index 2d4ec533e2d..fb455f2eb8c 100644 --- a/src/pages/wallets/find-wallet.tsx +++ b/src/pages/wallets/find-wallet.tsx @@ -44,6 +44,8 @@ import { WALLETS_FILTERS_DEFAULT, } from "@/lib/constants" +import { useWalletPersonas } from "../../hooks/useWalletPersonas" + import { WalletSupportedLanguageContext } from "@/contexts/WalletSupportedLanguageContext" import { useWalletTable } from "@/hooks/useWalletTable" import HeroImage from "@/public/images/wallets/wallet-hero.png" @@ -106,11 +108,11 @@ const FindWalletPage = ({ }: InferGetStaticPropsType) => { const { pathname } = useRouter() const { t } = useTranslation("page-wallets-find-wallet") - + const personas = useWalletPersonas() const resetWalletFilter = useRef(() => {}) const [filters, setFilters] = useState(WALLETS_FILTERS_DEFAULT) - const [selectedPersona, setSelectedPersona] = useState(NaN) + const [selectedPersona, setSelectedPersona] = useState([]) const [supportedLanguage, setSupportedLanguage] = useState(DEFAULT_LOCALE) const { isOpen: showMobileSidebar, onOpen, onClose } = useDisclosure() @@ -122,10 +124,36 @@ const FindWalletPage = ({ walletCardData, } = useWalletTable({ filters, supportedLanguage, t, walletData: wallets }) + const updatePersonaUponFilterChange = (filters) => { + const newSelectedPersona: number[] = [] + const trueFilters = Object.fromEntries( + Object.entries(filters).filter(([_, value]) => value) + ) + if (Object.keys(trueFilters).length === 0) { + setSelectedPersona([]) + return + } + + for (let i = 0; i < personas.length; i++) { + const truePresetFilters = Object.fromEntries( + Object.entries(personas[i].presetFilters).filter(([_, value]) => value) + ) + const isPersonaSelected = Object.entries(truePresetFilters).every( + ([key, value]) => trueFilters[key] === value + ) + if (isPersonaSelected) { + newSelectedPersona.push(i) + } + } + + setSelectedPersona(newSelectedPersona) + } + const updateFilterOption = (key) => { const updatedFilters = { ...filters } updatedFilters[key] = !updatedFilters[key] setFilters(updatedFilters) + updatePersonaUponFilterChange(updatedFilters) } const updateFilterOptions = (keys, value) => { @@ -134,10 +162,11 @@ const FindWalletPage = ({ updatedFilters[key] = value } setFilters(updatedFilters) + updatePersonaUponFilterChange(updatedFilters) } const resetFilters = () => { - setSelectedPersona(NaN) + setSelectedPersona([]) setFilters(WALLETS_FILTERS_DEFAULT) setSupportedLanguage(DEFAULT_LOCALE) } @@ -212,6 +241,7 @@ const FindWalletPage = ({ selectedPersona={selectedPersona} setSelectedPersona={setSelectedPersona} showMobileSidebar={showMobileSidebar} + resetWalletFilter={resetWalletFilter} /> diff --git a/src/pages/wallets/index.tsx b/src/pages/wallets/index.tsx index c238bfed980..a3b4cdfd40b 100644 --- a/src/pages/wallets/index.tsx +++ b/src/pages/wallets/index.tsx @@ -170,20 +170,20 @@ const WalletsPage = () => { href: "/wallets/find-wallet/", content: t("page-wallets-find-wallet-link"), matomo: { - eventCategory: "wallet hero buttons", + eventCategory: "Header buttons", eventAction: "click", - eventName: "find wallet", + eventName: "Find_wallet", }, }, { href: `#${SIMULATOR_ID}`, content: "How to use a wallet", matomo: { - eventCategory: "wallet hero buttons", + eventCategory: "Header buttons", eventAction: "click", - eventName: "interactive tutorial", + eventName: "How_to_use_wallet", }, - variant: "outline", + variant: "outline" as const, }, ] : [ @@ -191,9 +191,9 @@ const WalletsPage = () => { href: "/wallets/find-wallet/", content: t("page-wallets-find-wallet-link"), matomo: { - eventCategory: "wallet hero buttons", + eventCategory: "Header button", eventAction: "click", - eventName: "find wallet", + eventName: "Find_wallet", }, }, ], @@ -253,11 +253,21 @@ const WalletsPage = () => { title: t("page-wallets-protecting-yourself"), description: "MyCrypto", link: "https://support.mycrypto.com/staying-safe/protecting-yourself-and-your-funds", + customEventOptions: { + eventCategory: "Link", + eventAction: "Clicked_external", + eventName: "protecting_yourself", + }, }, { title: t("page-wallets-keys-to-safety"), description: t("page-wallets-blog"), link: "https://www.coinbase.com/learn/crypto-basics/how-to-secure-crypto", + customEventOptions: { + eventCategory: "Link", + eventAction: "Clicked_external", + eventName: "the_keys_to_keeping_crypto_safe", + }, }, ] @@ -265,10 +275,20 @@ const WalletsPage = () => { { title: t("additional-reading-how-to-create-an-ethereum-account"), link: "/guides/how-to-create-an-ethereum-account/", + customEventOptions: { + eventCategory: "Link", + eventAction: "Clicked", + eventName: "Create_eth_acc", + }, }, { title: t("additional-reading-how-to-use-a-wallet"), link: "/guides/how-to-use-a-wallet/", + customEventOptions: { + eventCategory: "Link", + eventAction: "Clicked", + eventName: "How_to_use_wallet", + }, }, ] @@ -400,7 +420,14 @@ const WalletsPage = () => { > {t("page-wallets-features-desc")} - + {t("page-wallets-find-wallet-btn")} ( - +

) const Title = (props: ChildOnlyProp) => ( - ) const Subtitle = (props: ChildOnlyProp) => ( - +

) const Hero = (props: ChildOnlyProp) => ( - ) -const Summary = (props: BoxProps) => ( - +const Summary = ({ className, ...rest }: HTMLAttributes) => ( +

) const Content = (props: ChildOnlyProp) => ( - +
) -const TwoColumnContent = (props: FlexProps) => ( +const TwoColumnContent = ({ + className, + ...rest +}: HTMLAttributes) => ( ) -const Section = (props: BoxProps) => +const Section = ({ className, ...rest }: HTMLAttributes) => ( +
+) export const Width60 = (props: ChildOnlyProp) => ( - +
) export const Width40 = (props: ChildOnlyProp) => ( -
+
) -const H2 = (prop: ChildOnlyProp & HeadingProps) => ( - ) => ( +

) const H3 = (props: ChildOnlyProp) => ( - +

) -const CardContainer = (props: ChildOnlyProp) => ( - +const CardContainer = ({ + className, + ...rest +}: HTMLAttributes) => ( + ) const Column = (props: ChildOnlyProp) => ( - +
) const StatPrimary = (props: ChildOnlyProp) => ( - +
) const StatDescription = (props: ChildOnlyProp) => ( - +
) -const ButtonRow = (props: ChildOnlyProp) => ( - +const ButtonRow = ({ className, ...rest }: HTMLAttributes) => ( + ) const NoWrapText = (props: ChildOnlyProp) => ( - + ) const Image400 = ({ src }: Pick) => ( - + ) -const cachedFetchTxCount = runOnlyOnce(fetchTxCount) +const cachedFetchTxCount = runOnlyOnce(fetchGrowThePie) type Props = BasePageProps & { data: MetricReturnData @@ -209,7 +181,7 @@ export const getStaticProps = (async ({ locale }) => { ...(await serverSideTranslations(locale!, requiredNamespaces)), contentNotTranslated, lastDeployLocaleTimestamp, - data, + data: data.txCount, }, } }) satisfies GetStaticProps @@ -279,26 +251,26 @@ const WhatIsEthereumPage = ({ title: t("page-what-is-ethereum-blockchain-tab-title"), eventName: "Blockchain tab", content: ( - +

- +

), }, { title: t("page-what-is-ethereum-cryptocurrency-tab-title"), eventName: "Cryptocurrency tab", content: ( - <> - + +

- - +

+

- - +

+

- - +

+
), }, ] @@ -320,524 +292,546 @@ const WhatIsEthereumPage = ({ ) return ( - - - - - - {t("page-what-is-ethereum-title")} - {t("page-what-is-ethereum-desc")} - {t("page-what-is-ethereum-subtitle")} - - - - - - {t("page-what-is-ethereum-alt-img-bazaar")} - - - - -
- - - - - {t("page-what-is-ethereum-summary-title")} - - {t("page-what-is-ethereum-summary-desc-1")} - {t("page-what-is-ethereum-summary-desc-2")} - {t("page-what-is-ethereum-summary-desc-3")} - - - - - -
-
- - -

{t("page-what-is-ethereum-what-can-eth-do-title")}

- - {cards.map((card, idx) => ( - - ))} - -
- - - - { - trackCustomEvent({ - eventCategory: `Blockchain/crypto tab`, - eventAction: `Clicked`, - eventName: tabs[index].eventName, - }) - }} - tabs={tabs} + + + + + + +
+ {t("page-what-is-ethereum-title")} + + {t("page-what-is-ethereum-desc")} + {t("page-what-is-ethereum-subtitle")} + + + + +
+
+ + -
- -
-
- -
- - -

- {t("page-what-is-ethereum-why-would-i-use-ethereum-title")} -

- - {t("page-what-is-ethereum-why-would-i-use-ethereum-1")} - - - {t("page-what-is-ethereum-why-would-i-use-ethereum-2")} - - - { - trackCustomEvent({ - eventCategory: `What is Ethereum - Slider`, - eventAction: `Clicked`, - eventName: slides[index].eventName, - }) - }} - > - -

{t("page-what-is-ethereum-slide-1-title")}

- - - - {t("page-what-is-ethereum-slide-1-desc-2")} -
- -

{t("page-what-is-ethereum-slide-2-title")}

- {t("page-what-is-ethereum-slide-2-desc-1")} - - - -
- -

{t("page-what-is-ethereum-slide-3-title")}

- - - -
- -

{t("page-what-is-ethereum-slide-4-title")}

- {t("page-what-is-ethereum-slide-4-desc-1")} - {t("page-what-is-ethereum-slide-4-desc-2")} -
-
-
- - - -
-
+ +
+ +
+
+ + + + + +

+ {t("page-what-is-ethereum-summary-title")} +

+ +

{t("page-what-is-ethereum-summary-desc-1")}

+

{t("page-what-is-ethereum-summary-desc-2")}

+

{t("page-what-is-ethereum-summary-desc-3")}

+
+
+
+
+ +
+ +
+

{t("page-what-is-ethereum-what-can-eth-do-title")}

+ + {cards.map((card, idx) => ( + + ))} + +
+ + + + { + trackCustomEvent({ + eventCategory: `Blockchain/crypto tab`, + eventAction: `Clicked`, + eventName: tabs[index].eventName, + }) + }} + tabs={tabs} + /> + + + +
+
+
+ +
+ + +

+ {t("page-what-is-ethereum-why-would-i-use-ethereum-title")} +

+ +

{t("page-what-is-ethereum-why-would-i-use-ethereum-1")}

+

{t("page-what-is-ethereum-why-would-i-use-ethereum-2")}

+ + { + trackCustomEvent({ + eventCategory: `What is Ethereum - Slider`, + eventAction: `Clicked`, + eventName: slides[index].eventName, + }) + }} + > + +

{t("page-what-is-ethereum-slide-1-title")}

+ +

+ +

+

{t("page-what-is-ethereum-slide-1-desc-2")}

+
+
+ +

{t("page-what-is-ethereum-slide-2-title")}

+ +

{t("page-what-is-ethereum-slide-2-desc-1")}

+

+ +

+
+
+ +

{t("page-what-is-ethereum-slide-3-title")}

+ +

+ +

+
+
+ +

{t("page-what-is-ethereum-slide-4-title")}

+ +

{t("page-what-is-ethereum-slide-4-desc-1")}

+

{t("page-what-is-ethereum-slide-4-desc-2")}

+
+
+
+
+
+ + + +
+
+ +
+ + +

{t("page-what-is-ethereum-ethereum-in-numbers-title")}

+ + + {formatNumber(4000, 1, 1)}+ + + {t( + "page-what-is-ethereum-ethereum-in-numbers-stat-1-desc" + )} + +   + + + + + + + + + + {formatNumber(96_000_000, 2, 2)}+ + + {t( + "page-what-is-ethereum-ethereum-in-numbers-stat-2-desc" + )} + +   + + + + + + + + + + {formatNumber(53_300_000, 3, 3)}+ + + {t( + "page-what-is-ethereum-ethereum-in-numbers-stat-3-desc" + )} + +   + + + + + + + + + + + {formatNumber(410_000_000_000, 3, 3, "currency", "USD")} + + + {t( + "page-what-is-ethereum-ethereum-in-numbers-stat-4-desc" + )} + +   + + + + + + + + + + + {formatNumber(3_500_000_000, 2, 2, "currency", "USD")} + + + {t( + "page-what-is-ethereum-ethereum-in-numbers-stat-5-desc" + )} + +   + + + + + + + + + + + {txStat || } + + {/* TODO: Extract strings for translation */} + + {t( + "page-what-is-ethereum-ethereum-in-numbers-stat-6-desc" + )} + +   + + + + + + + + + +
+ + + +
+
+ +
+ + + + + +

{t("page-what-is-ethereum-who-runs-ethereum-title")}

+ +

+ +

+

+ +

+ + + {t("page-what-is-ethereum-run-a-node")} + + +
+
+
+
+ +
+ + + + + +

{t("page-what-is-ethereum-smart-contract-title")}

+ +

+ +

+

+ +

+

+ +

+ + + {t("page-what-is-ethereum-more-on-smart-contracts")} + + + {t("page-what-is-ethereum-explore-dapps")} + + +
+
+
+
+ +
+ + + + + +

{t("page-what-is-ethereum-meet-ether-title")}

+ +

{t("page-what-is-ethereum-meet-ether-desc-1")}

+

{t("page-what-is-ethereum-meet-ether-desc-2")}

+ + + {t("page-what-is-ethereum-what-is-ether")} + + + {t("page-what-is-ethereum-get-eth")} + + +
+
+
+
+ +
+ + + + + +

{t("page-what-is-ethereum-energy-title")}

+ +

+ +

+

+ +

+ + + {t("page-what-is-ethereum-more-on-energy-consumption")} + + + {t("page-what-is-ethereum-the-merge-update")} + + +
+
+
+
+ +
+ + + + + +

{t("page-what-is-ethereum-criminal-activity-title")}

+ +

{t("page-what-is-ethereum-criminal-activity-desc-1")}

+

{t("page-what-is-ethereum-criminal-activity-desc-2")}

+

+ + {t("page-what-is-ethereum-criminal-activity-desc-3")} + +

+
    +
  • + + Europol Spotlight - Cryptocurrencies - Tracing the + evolution of criminal finances.pdf + {" "} + EN (1.4 MB) +
  • +
+
+
+
+
+ +
+ + + + + +

{t("page-what-is-ethereum-btc-eth-diff-title")}

+ +

{t("page-what-is-ethereum-btc-eth-diff-1")}

+

+ +

+

{t("page-what-is-ethereum-btc-eth-diff-3")}

+

{t("page-what-is-ethereum-btc-eth-diff-4")}

+
+
+
+
+
- - -

{t("page-what-is-ethereum-ethereum-in-numbers-title")}

- - - {formatNumber(4000, 1, 1)}+ - - {t("page-what-is-ethereum-ethereum-in-numbers-stat-1-desc")} - -   - - - - - - - - - - {formatNumber(96_000_000, 2, 2)}+ - - {t("page-what-is-ethereum-ethereum-in-numbers-stat-2-desc")} - -   - - - - - - - - - - {formatNumber(53_300_000, 3, 3)}+ - - {t("page-what-is-ethereum-ethereum-in-numbers-stat-3-desc")} - -   - - - - - - - - - - - {formatNumber(410_000_000_000, 3, 3, "currency", "USD")} - - - {t("page-what-is-ethereum-ethereum-in-numbers-stat-4-desc")} - -   - - - - - - - - - - - {formatNumber(3_500_000_000, 2, 2, "currency", "USD")} - - - {t("page-what-is-ethereum-ethereum-in-numbers-stat-5-desc")} - -   - - - - - - - - - - - {txStat || } - - {/* TODO: Extract strings for translation */} - - {t("page-what-is-ethereum-ethereum-in-numbers-stat-6-desc")} - -   - - - - - - - - - -
- - - -
-
- -
- - - - - -

{t("page-what-is-ethereum-who-runs-ethereum-title")}

- - - - - - - - - {t("page-what-is-ethereum-run-a-node")} - - -
-
+

{t("page-what-is-ethereum-additional-reading")}

+ +

+ + {t("page-what-is-ethereum-week-in-ethereum")} + {" "} + {t("page-what-is-ethereum-week-in-ethereum-desc")} +

+

+ + {t("page-what-is-ethereum-atoms-institutions-blockchains")} + {" "} + {t("page-what-is-ethereum-atoms-institutions-blockchains-desc")} +

+ +

+ + {t("page-what-is-ethereum-kernel-dreamers")} + {" "} + {t("page-what-is-ethereum-kernel-dreamers-desc")} +

+
- - - - - -

{t("page-what-is-ethereum-smart-contract-title")}

- - - - - - - - - - - - {t("page-what-is-ethereum-more-on-smart-contracts")} - - - {t("page-what-is-ethereum-explore-dapps")} - - -
-
-
- -
- - - - - -

{t("page-what-is-ethereum-meet-ether-title")}

- {t("page-what-is-ethereum-meet-ether-desc-1")} - {t("page-what-is-ethereum-meet-ether-desc-2")} - - - {t("page-what-is-ethereum-what-is-ether")} - - + +

{t("page-what-is-ethereum-explore")}

+
+ + +
+ {t("page-what-is-ethereum-get-eth")} - - - -
- -
- - - - - -

{t("page-what-is-ethereum-energy-title")}

- - - - - - - - - {t("page-what-is-ethereum-more-on-energy-consumption")} - - - {t("page-what-is-ethereum-the-merge-update")} +
+ + +
+ + {t("page-what-is-ethereum-explore-dapps")} - - - +
+
+

- - - - - -

{t("page-what-is-ethereum-criminal-activity-title")}

- {t("page-what-is-ethereum-criminal-activity-desc-1")} - {t("page-what-is-ethereum-criminal-activity-desc-2")} - - - {t("page-what-is-ethereum-criminal-activity-desc-3")} - - - - - - Europol Spotlight - Cryptocurrencies - Tracing the evolution - of criminal finances.pdf - {" "} - EN (1.4 MB) - - -
-
+
- - - - - -

{t("page-what-is-ethereum-btc-eth-diff-title")}

- {t("page-what-is-ethereum-btc-eth-diff-1")} - - - - {t("page-what-is-ethereum-btc-eth-diff-3")} - {t("page-what-is-ethereum-btc-eth-diff-4")} -
-
+
-
- - -

{t("page-what-is-ethereum-additional-reading")}

- - - {t("page-what-is-ethereum-week-in-ethereum")} - {" "} - {t("page-what-is-ethereum-week-in-ethereum-desc")} - - - - {t("page-what-is-ethereum-atoms-institutions-blockchains")} - {" "} - {t("page-what-is-ethereum-atoms-institutions-blockchains-desc")} - - - - - {t("page-what-is-ethereum-kernel-dreamers")} - {" "} - {t("page-what-is-ethereum-kernel-dreamers-desc")} - -
- - - -

{t("page-what-is-ethereum-explore")}

-
- - - - - {t("page-what-is-ethereum-get-eth")} - - - - - - - {t("page-what-is-ethereum-explore-dapps")} - - - - -
- - - - - - - - -
+ + ) } diff --git a/src/styles/colors.css b/src/styles/colors.css new file mode 100644 index 00000000000..46942217230 --- /dev/null +++ b/src/styles/colors.css @@ -0,0 +1,87 @@ +@tailwind base; + +/* FOUNDATIONAL COLOR PALETTE DECLARATIONS */ + +@layer base { + :root { + --white: 0, 0%, 100%; /* #ffffff */ + --gray-50: 0, 0%, 97%; /* #f7f7f7 */ + --gray-100: 0, 0%, 93%; /* #eeeeee */ + --gray-150: 0, 0%, 93%; /* #ececec */ /* TODO: Confirm this shade, used in nav menu */ + --gray-200: 0, 0%, 81%; /*#cecece */ + --gray-300: 0, 0%, 67%; /* #acacac */ + --gray-400: 0, 0%, 55%; /* #8C8C8C */ + --gray-500: 0, 0%, 38%; /* #616161 */ + --gray-600: 0, 0%, 20%; /* #333333 */ + --gray-700: 0, 0%, 13%; /* #222222 */ + --gray-800: 0, 0%, 11%; /* #1b1b1b */ + --gray-900: 0, 0%, 7%; /* #121212 */ + --gray-950: 0, 0%, 4%; /* #0a0a0a */ + --black: 0, 0%, 0%; /* #000000 */ + + --purple-50: 262, 100%, 96%; /* #F3ECFF */ + --purple-100: 263, 100%, 94%; /* #EDE2FF */ + --purple-200: 263, 91%, 88%; /* #DAC5FC */ + --purple-300: 263, 94%, 84%; /* #CCAFFC */ + --purple-400: 263, 77%, 75%; /* #B38DF0 */ + --purple-500: 263, 88%, 65%; /* #945AF4 */ + --purple-600: 263, 75%, 51%; /* #6C24DF */ + --purple-700: 263, 74%, 41%; /* #561BB5 */ + --purple-800: 263, 77%, 31%; /* #41128B */ + --purple-900: 263, 86%, 15%; /* #1E0546 */ + + --pink-50: 325, 63%, 93%; /* #F8E0EE */ + --pink-100: 322, 78%, 87%; /* #F8C5E5 */ + --pink-200: 323, 100%, 85%; /* #FFB2E2 */ + --pink-300: 323, 100%, 83%; /* #FFA6DD */ + --pink-400: 323, 91%, 75%; /* #F986CD */ + --pink-500: 323, 100%, 66%; /* #FF51BC */ + --pink-600: 323, 93%, 51%; /* #F6109E */ + --pink-700: 323, 99%, 39%; /* #C7017B */ + --pink-800: 323, 87%, 29%; /* #8C0A5A */ + --pink-900: 323, 82%, 18%; /* #530836 */ + + --blue-50: 214, 100%, 99%; /* #F8FBFF */ + --blue-100: 217, 100%, 95%; /* #E8F1FF */ + --blue-200: 214, 86%, 89%; /* #CADFFB */ + --blue-300: 221, 79%, 74%; /* #88AAF1 */ + --blue-400: 221, 90%, 69%; /* #6995F7 */ + --blue-500: 224, 84%, 60%; /* #4473EF */ + --blue-600: 235, 81%, 58%; /* #3C4CEB */ + --blue-700: 235, 59%, 41%; /* #2B36A8 */ + --blue-800: 231, 53%, 29%; /* #232F71 */ + --blue-900: 217, 36%, 17%; /* #1B273A */ + + --teal-50: 164, 100%, 98%; /* #F4FFFC */ + --teal-100: 164, 79%, 95%; /* #E6FCF6 */ + --teal-200: 163, 81%, 85%; /* #BBF8E7 */ + --teal-300: 163, 96%, 78%; /* #91FDDE */ + --teal-400: 163, 90%, 65%; /* #58F6C9 */ + --teal-500: 163, 76%, 48%; /* #1DD8A3 */ + --teal-600: 163, 88%, 39%; /* #0CB988 */ + --teal-700: 163, 82%, 33%; /* #0F9971 */ + --teal-800: 163, 93%, 21%; /* #04674B */ + --teal-900: 162, 97%, 13%; /* #01422F */ + + --orange-100: 30, 100%, 94%; /* #FFF0DB */ + --orange-200: 30, 100%, 82%; /* #FFD7A7 */ + --orange-300: 30, 98%, 70%; /* #FEB077 */ + --orange-400: 30, 97%, 58%; /* #FD8640 */ + --orange-500: 30, 95%, 51%; /* #FB610E */ + --orange-600: 20, 95%, 47%; /* #EC4A0A */ + --orange-700: 15, 90%, 39%; /* #C4350A */ + --orange-800: 10, 76%, 28%; /* #7D2711 */ + --orange-900: 20, 33%, 15%; /* #3A291D */ + + /* TODO: Update to new color theming */ + --red-100: 0, 75%, 88%; /* #f7c8c8 */ + --red-500: 0, 100%, 36%; /* #b80000 */ + --red-800: 0, 33%, 7%; /* #180c0c */ + --green-100: 138, 51%, 91%; /* #ddf4e4 */ + --green-500: 155, 84%, 24%; /* #0a7146 */ + --green-900: 140, 37%, 6%; /* #0a160e */ + --yellow-200: 47, 100%, 94%; /* #fff8df */ + --yellow-500: 42, 100%, 37%; /* #bd8400 */ + + } +} diff --git a/src/styles/config.ts b/src/styles/config.ts new file mode 100644 index 00000000000..8ce8205b09d --- /dev/null +++ b/src/styles/config.ts @@ -0,0 +1,7 @@ +import resolveConfig from "tailwindcss/resolveConfig" + +import tailwindConfig from "../../tailwind.config" + +const config = resolveConfig(tailwindConfig) + +export default config diff --git a/src/styles/docsearch.css b/src/styles/docsearch.css index dfce14f0ccb..91f6906a80d 100644 --- a/src/styles/docsearch.css +++ b/src/styles/docsearch.css @@ -38,7 +38,8 @@ --docsearch-modal-width: 650px; --docsearch-hit-height: fit-content; } -html[data-theme="dark"] { + +.dark { --docsearch-modal-background: theme(backgroundColor.background.DEFAULT); --docsearch-highlight-color: theme(colors.primary.hover); } diff --git a/src/styles/fonts.css b/src/styles/fonts.css index d9e62abd6a8..213b5fbc7fd 100644 --- a/src/styles/fonts.css +++ b/src/styles/fonts.css @@ -1,4 +1,4 @@ -/* css imported from https://fonts.googleapis.com/css2?family=Inter:wght@400;700 */ +/* css imported from https://fonts.googleapis.com/css2?family=Inter:wght@400;700;900 */ /* cyrillic-ext */ @font-face { @@ -9,7 +9,7 @@ src: url(/fonts/inter/cyrillic-ext.woff2) format("woff2"); unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; - } +} /* cyrillic */ @font-face { font-family: "Inter"; @@ -36,7 +36,7 @@ font-display: swap; src: url(/fonts/inter/greek.woff2) format("woff2"); unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, - U+03A3-03FF; + U+03A3-03FF; } /* vietnamese */ @font-face { @@ -46,7 +46,7 @@ font-display: swap; src: url(/fonts/inter/vietnamese.woff2) format("woff2"); unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, - U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, + U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @@ -58,7 +58,7 @@ src: url(/fonts/inter/latin-ext.woff2) format("woff2"); unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; - } +} /* latin */ @font-face { font-family: "Inter"; @@ -67,9 +67,10 @@ font-display: swap; src: url(/fonts/inter/latin.woff2) format("woff2"); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, - U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, - U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, + U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } + /* cyrillic-ext */ @font-face { font-family: "Inter"; @@ -78,7 +79,7 @@ font-display: swap; src: url(/fonts/inter/cyrillic-ext.woff2) format("woff2"); unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, - U+FE2E-FE2F; + U+FE2E-FE2F; } /* cyrillic */ @font-face { @@ -106,7 +107,7 @@ font-display: swap; src: url(/fonts/inter/greek.woff2) format("woff2"); unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, - U+03A3-03FF; + U+03A3-03FF; } /* vietnamese */ @font-face { @@ -118,8 +119,8 @@ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; - } - /* latin-ext */ +} +/* latin-ext */ @font-face { font-family: "Inter"; font-style: normal; @@ -127,7 +128,7 @@ font-display: swap; src: url(/fonts/inter/latin-ext.woff2) format("woff2"); unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, - U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; + U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { @@ -137,49 +138,119 @@ font-display: swap; src: url(/fonts/inter/latin.woff2) format("woff2"); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, - U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, - U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, + U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +/* cyrillic-ext */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 900; + src: url(/fonts/inter/cyrillic-ext.woff2) format("woff2"); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, + U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 900; + src: url(/fonts/inter/cyrillic.woff2) format("woff2"); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 900; + src: url(/fonts/inter/greek-ext.woff2) format("woff2"); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 900; + src: url(/fonts/inter/greek.woff2) format("woff2"); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, + U+03A3-03FF; +} +/* vietnamese */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 900; + src: url(/fonts/inter/vietnamese.woff2) format("woff2"); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, + U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, + U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 900; + src: url(/fonts/inter/latin-ext.woff2) format("woff2"); + unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, + U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 900; + src: url(/fonts/inter/latin.woff2) format("woff2"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, + U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, + U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* css imported from https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400 */ /* cyrillic-ext */ @font-face { - font-family: 'IBM Plex Mono'; + font-family: "IBM Plex Mono"; font-style: normal; font-weight: 400; - src: url(/fonts/ibm-plex-mono/IBMPlexMono-Regular.ttf) format('truetype'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; + src: url(/fonts/ibm-plex-mono/IBMPlexMono-Regular.ttf) format("truetype"); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, + U+FE2E-FE2F; } /* cyrillic */ @font-face { - font-family: 'IBM Plex Mono'; + font-family: "IBM Plex Mono"; font-style: normal; font-weight: 400; - src: url(/fonts/ibm-plex-mono/IBMPlexMono-Regular.ttf) format('truetype'); + src: url(/fonts/ibm-plex-mono/IBMPlexMono-Regular.ttf) format("truetype"); unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* vietnamese */ @font-face { - font-family: 'IBM Plex Mono'; + font-family: "IBM Plex Mono"; font-style: normal; font-weight: 400; - src: url(/fonts/ibm-plex-mono/IBMPlexMono-Regular.ttf) format('truetype'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; + src: url(/fonts/ibm-plex-mono/IBMPlexMono-Regular.ttf) format("truetype"); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, + U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, + U+1EA0-1EF9, U+20AB; } /* latin-ext */ @font-face { - font-family: 'IBM Plex Mono'; + font-family: "IBM Plex Mono"; font-style: normal; font-weight: 400; - src: url(/fonts/ibm-plex-mono/IBMPlexMono-Regular.ttf) format('truetype'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; + src: url(/fonts/ibm-plex-mono/IBMPlexMono-Regular.ttf) format("truetype"); + unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, + U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'IBM Plex Mono'; + font-family: "IBM Plex Mono"; font-style: normal; font-weight: 400; - src: url(/fonts/ibm-plex-mono/IBMPlexMono-Regular.ttf) format('truetype'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} \ No newline at end of file + src: url(/fonts/ibm-plex-mono/IBMPlexMono-Regular.ttf) format("truetype"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, + U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, + U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} diff --git a/src/styles/global.css b/src/styles/global.css index cc8ed01a48c..2a7cc970e30 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -3,6 +3,8 @@ @tailwind utilities; @import "@docsearch/css"; +@import "@/styles/colors.css"; +@import "@/styles/semantic-tokens.css"; @import "@/styles/fonts.css"; @import "@/styles/docsearch.css"; @@ -11,82 +13,13 @@ --font-inter: Inter, sans-serif; --font-mono: "IBM Plex Mono", Courier, monospace; - /* Primitive Color Scheme */ - --gray-100: #f7f7f7; - --gray-150: #f2f2f2; - --gray-200: #e7e7e7; - --gray-300: #c8c8c8; - --gray-400: #8c8c8c; - --gray-500: #616161; - --gray-600: #333333; - --gray-700: #222222; - --gray-800: #1b1b1b; - --gray-900: #141414; - - --blue-50: #f6f6ff; - --blue-100: #ebebff; - --blue-200: #d6d6ff; - --blue-300: #9999ff; - --blue-400: #5555ff; - --blue-500: #1c1cff; - --blue-600: #000066; - --blue-700: #0000a3; - --blue-800: #000066; - --blue-900: #000029; - - --orange-50: #fff3ed; - --orange-100: #ffe5d6; - --orange-200: #ffcbad; - --orange-300: #ffb18f; - --orange-400: #ff985c; - --orange-500: #ff7324; - --orange-550: #df5a0e; - --orange-600: #b84300; - --orange-700: #7a2d00; - --orange-800: #521e00; - --orange-900: #2f1000; - - --red-100: #f7c8c8; - --red-500: #b80000; - /* ! Deprecating 900 */ - --red-900: #180c0c; - - --green-100: #ddf4e4; - /* ! Deprecating 400 */ - --green-400: #48bb78; - --green-500: #0a7146; - /* ! Deprecating 900 */ - --green-900: #0a160e; - - --yellow-200: #fff8df; - --yellow-500: #bd8400; - /* Semantic Colors: Light mode */ - --primary: var(--blue-500); - --primary-high-contrast: var(--blue-800); - --primary-low-contrast: var(--blue-100); - --primary-hover: var(--blue-400); - --primary-visited: var(--blue-700); /* ! Deprecating primary-light */ --primary-light: var(--blue-100); /* ! Deprecating primary-dark */ --primary-dark: var(--blue-700); - /* ! Deprecating primary-pressed */ - --primary-pressed: var(--blue-400); - - --body: var(--gray-800); - --body-medium: var(--gray-500); - --body-light: var(--gray-200); - /* ! Deprecating body-inverted */ - --body-inverted: var(--gray-100); - - --background: white; - --background-highlight: var(--gray-100); - --disabled: var(--gray-400); - - /* ! Deprecating neutral */ - --neutral: white; + --neutral: var(--white); /* Complementary Set */ --attention: var(--yellow-500); @@ -110,16 +43,25 @@ --tooltip-shadow: rgba(0, 0, 0, 0.24); --switch-background: var(--gray-300); --hub-hero-content-bg: rgba(255, 255, 255, 0.8); - --main-gradient: linear-gradient( + --gradient-main: linear-gradient( 102.7deg, rgba(185, 185, 241, 0.2) 0%, rgba(84, 132, 234, 0.2) 51.56%, rgba(58, 142, 137, 0.2) 100% ); - --feedback-gradient: var(--main-gradient); + --feedback-gradient: var(--gradient-main); --table-box-shadow: 0 14px 66px rgba(0, 0, 0, 0.07), 0 10px 17px rgba(0, 0, 0, 0.03), 0 4px 7px rgba(0, 0, 0, 0.05); --table-item-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); + + --gradient-banner: radial-gradient( + 155% 100% at 50% 0%, + rgba(201, 179, 245, 0.16) 0%, + rgba(201, 179, 245, 0.48) 33%, + rgba(136, 170, 241, 0.16) 66%, + rgba(255, 255, 255, 0) 100% + ); + --banner-grid-gradient: linear-gradient( 90deg, rgba(127, 127, 213, 0.2) 0%, @@ -129,30 +71,12 @@ --search-background: var(--background); } - [data-theme="dark"] { + .dark { /* Semantic Colors: Dark mode */ - --primary: var(--orange-500); - --primary-high-contrast: var(--orange-100); - --primary-low-contrast: var(--orange-800); - --primary-hover: var(--orange-400); - --primary-visited: var(--orange-550); /* ! Deprecating primary-light */ - --primary-light: var(--orange-100); + --primary-light: hsla(var(--orange-100)); /* ! Deprecating primary-dark */ - --primary-dark: var(--orange-800); - /* ! Deprecating primary-pressed */ - --primary-pressed: var(--orange-800); - - --body: var(--gray-100); - --body-medium: var(--gray-400); - --body-light: var(--gray-600); - /* ! Deprecating body-inverted */ - --body-inverted: var(--gray-800); - - --background: var(--gray-800); - --background-highlight: var(--gray-900); - - --disabled: var(--gray-500); + --primary-dark: hsla(var(--orange-800)); /* ! Deprecating neutral */ --neutral: var(--gray-900); @@ -170,11 +94,11 @@ /* ! Deprecating success-neutral */ --success-neutral: var(--green-900); - /* Misc sematics: light mode */ + /* Misc sematics: dark mode */ --tooltip-shadow: rgba(255, 255, 255, 0.24); --switch-background: rgba(255, 255, 255, 0.24); --hub-hero-content-bg: rgba(34, 34, 34, 0.8); - --main-gradient: linear-gradient( + --gradient-main: linear-gradient( 102.7deg, rgba(185, 185, 241, 0.2) 0%, rgba(84, 132, 234, 0.2) 51.56%, @@ -200,11 +124,11 @@ @layer base { body { - @apply bg-background font-body text-sm text-body lg:text-md; + @apply !bg-background font-body text-sm !text-body lg:text-md; } a { - @apply underline-offset-3 text-primary underline hover:text-primary-hover focus-visible:rounded-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-hover; + @apply text-primary underline underline-offset-3 hover:text-primary-hover focus-visible:rounded-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-hover; } h1, @@ -278,3 +202,70 @@ } } } + +@layer components { + .swiper-horizontal > .swiper-pagination-bullets, + .swiper-pagination-bullets.swiper-pagination-horizontal { + @apply bg-background-highlight; + } + + .swiper-pagination.swiper-pagination-clickable.swiper-pagination-bullets.swiper-pagination-horizontal { + @apply !relative; + } + + .swiper-pagination { + @apply relative mx-auto mt-8 flex h-[26px] max-w-48 items-center justify-center rounded-full bg-background-highlight; + } + + .css-posts-swiper .swiper-pagination { + @apply max-w-52 sm:max-w-40 lg:max-w-36; + } + + .swiper-pagination .swiper-pagination-bullet { + @apply bg-primary-high-contrast; + } + + .swiper-pagination.swiper-pagination-clickable.swiper-pagination-bullets.swiper-pagination-horizontal + .swiper-pagination-bullet-active { + @apply bg-primary-hover; + } + + .swiper-button-prev, + .swiper-button-next { + @apply !h-6 !w-fit fill-primary px-2; + } +} + +.swiper-button-prev, +.swiper-button-next { + top: calc(100% - 11px) !important; + --nav-inset: calc(50% - 6.5rem); +} + +@media (min-width: theme("screens.sm")) { + .swiper-button-prev, + .swiper-button-next { + --nav-inset: calc(50% - 5rem); + } +} + +@media (min-width: theme("screens.lg")) { + .swiper-button-prev, + .swiper-button-next { + --nav-inset: calc(50% - 4.5rem); + } +} + +.swiper-button-next { + inset-inline-end: var(--nav-inset) !important; + inset-inline-start: auto !important; +} + +.swiper-button-prev { + inset-inline-end: auto !important; + inset-inline-start: var(--nav-inset) !important; +} + +.swiper-slide-shadow { + background: transparent !important; +} diff --git a/src/styles/semantic-tokens.css b/src/styles/semantic-tokens.css new file mode 100644 index 00000000000..e8400ac7236 --- /dev/null +++ b/src/styles/semantic-tokens.css @@ -0,0 +1,131 @@ +@tailwind base; + +/* SEMANTIC TOKEN DECLARATIONS: Abstracted from color palettes for theming */ + +@layer base { + /* Light mode (default) token declarations */ + :root { + --body: var(--gray-900); + --body-medium: var(--gray-500); + --body-light: var(--gray-200); + --body-inverse: var(--white); + --disabled: var(--gray-400); + --background: var(--white); + --background-highlight: var(--gray-50); + + --primary: var(--purple-600); + --primary-high-contrast: var(--purple-800); + --primary-low-contrast: var(--purple-100); + --primary-hover: var(--purple-500); + --primary-visited: var(--purple-700); + --primary-action: var(--purple-600); + --primary-action-hover: var(--purple-500); + + --accent-a: var(--blue-600); + --accent-a-hover: var(--blue-500); + + --accent-b: var(--pink-600); + --accent-b-hover: var(--pink-500); + + --accent-c: var(--teal-700); + --accent-c-hover: var(--teal-600); + + /** + /* Gradients (radial, conic) + /* For linear-gradient, Tailwind classes preferred + /* https://tailwindcss.com/docs/background-image#linear-gradients + */ + --radial-a-opacity-1: 0.08; + --radial-a-opacity-2: 0.24; + --radial-a: radial-gradient( + 127.67% 82.36% at 50% -30.36%, + hsla(var(--purple-500), var(--radial-a-opacity-1)) 0%, + hsla(var(--purple-500), var(--radial-a-opacity-2)) 33%, + hsla(var(--blue-500), var(--radial-a-opacity-1)) 66%, + transparent 100% + ); + + /* Shadows */ + --shadow-color-a: hsla(var(--purple-800), 0.02); + --shadow-color-b: hsla(var(--red-800), 0.04); + --shadow-color-c: hsla(var(--purple-700), 0.04); + --shadow-color-d: hsla(var(--purple-100), 0.08); + + --shadow-svg-button-link-1: 2px 2px 12px 1px var(--shadow-color-a); + --shadow-svg-button-link-2: 12px 16px 12px -3px var(--shadow-color-a); + --shadow-svg-button-link-3: 24px 32px 24px -6px var(--shadow-color-a); + --shadow-svg-button-link-4: 32px 40px 40px -12px var(--shadow-color-b); + --shadow-svg-button-link-1-hover: 0px 0px 12px 2px var(--shadow-color-a); + --shadow-svg-button-link-2-hover: 0px 0px 12px 2px var(--shadow-color-a); + --shadow-svg-button-link-3-hover: 0px 0px 24px 2px var(--shadow-color-a); + --shadow-svg-button-link-4-hover: 0px 0px 40px 2px var(--shadow-color-b); + + --shadow-body-color-a: hsla(var(--gray-900), 0.25); + --shadow-body-color-b: hsla(var(--gray-900), 0.05); + --shadow-body-md: 0px 4px 4px 0px var(--shadow-body-color-a); + --shadow-body-lg: 0px -6px 10px 0px var(--shadow-body-color-b); + + --shadow-window-box-1-opacity: 0.02; + --shadow-window-box-2-opacity: 0.02; + --shadow-window-box-3-opacity: 0.02; + --shadow-window-box-4-opacity: 0.04; + --shadow-window-box-5-opacity: 0.08; + --shadow-window-box-1: 0px 2px 12px 1px + hsla(var(--purple-800), var(--shadow-window-box-1-opacity)); + --shadow-window-box-2: 0px 16px 12px -3px hsla(var(--purple-800), var(--shadow-window-box-2-opacity)); + --shadow-window-box-3: 0px 32px 24px -6px hsla(var(--purple-800), var(--shadow-window-box-3-opacity)); + --shadow-window-box-4: 0px 40px 40px -12px hsla(var(--purple-700), var(--shadow-window-box-4-opacity)); + --shadow-window-box-5: 0px -64px 120px 80px hsla(var(--purple-100), var(--shadow-window-box-5-opacity)); + } + + /* Dark mode token declarations */ + .dark { + --body: var(--gray-100); + --body-medium: var(--gray-400); + --body-light: var(--gray-600); + --body-inverse: var(--black); + --disabled: var(--gray-500); + + --background: var(--black); + --background-highlight: var(--gray-900); + + --primary: var(--purple-400); + --primary-high-contrast: var(--purple-200); + --primary-low-contrast: var(--purple-900); + --primary-hover: var(--purple-300); + --primary-visited: var(--purple-500); + /* TODO: hover same as action in dark mode: */ + + --accent-a: var(--blue-400); + --accent-a-hover: var(--blue-300); + + --accent-b: var(--pink-400); + --accent-b-hover: var(--pink-300); + + --accent-c: var(--teal-400); + --accent-c-hover: var(--teal-300); + + /* Gradients (dark mode adjustments) */ + --radial-a-opacity-1: 0.16; + --radial-a-opacity-2: 0.48; + + /* Shadows (dark mode adjustments) */ + --shadow-color: hsla(var(--white), 0.04); + --shadow-svg-button-link-1: -2px 2px 12px 1px var(--shadow-color); + --shadow-svg-button-link-2: -6px 6px 12px -3px var(--shadow-color); + --shadow-svg-button-link-3: -12px 12px 24px -6px var(--shadow-color); + --shadow-svg-button-link-4: -20px 20px 40px -12px var(--shadow-color); + --shadow-svg-button-link-1-hover: 0px 0px 12px 2px var(--shadow-color); + --shadow-svg-button-link-2-hover: 0px 0px 12px 2px var(--shadow-color); + --shadow-svg-button-link-3-hover: 0px 0px 24px 2px var(--shadow-color); + --shadow-svg-button-link-4-hover: 0px 0px 40px 2px var(--shadow-color); + --shadow-body-color-a: hsla(var(--gray-600), 0.25); + --shadow-body-color-b: hsla(var(--gray-600), 0.05); + + --shadow-window-box-1-opacity: 0.1; + --shadow-window-box-2-opacity: 0.08; + --shadow-window-box-3-opacity: 0.16; + --shadow-window-box-4-opacity: 0.06; + --shadow-window-box-5-opacity: 0.06; + } +} diff --git a/tailwind.config.ts b/tailwind.config.ts index 0a6d8b60e8e..9ad06a6a1ca 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -4,7 +4,8 @@ import plugin from "tailwindcss/plugin" import { screens } from "./tailwind/screens" const config = { - darkMode: ["selector", "[data-theme='dark']"], + // TODO: Move to "class" strategy after removing Chakra + darkMode: ["selector", '[data-theme="dark"]'], content: [ "./src/**/*.{ts,tsx}", // TODO: remove after migration @@ -20,6 +21,7 @@ const config = { monospace: "var(--font-mono)", }, fontSize: { + "7xl": ["4rem", "1.1"], // [7xl, 6xs] "6xl": ["3.75rem", "1.2"], // [6xl, 4xs] "5xl": ["3rem", "1.2"], // [5xl, 4xs] "4xl": ["2.25rem", "1.2"], // [4xl, 4xs] @@ -59,117 +61,165 @@ const config = { }, colors: { gray: { - 100: "var(--gray-100)", - 150: "var(--gray-150)", - 200: "var(--gray-200)", - 300: "var(--gray-300)", - 400: "var(--gray-400)", - 500: "var(--gray-500)", - 600: "var(--gray-600)", - 700: "var(--gray-700)", - 800: "var(--gray-800)", - 900: "var(--gray-900)", + 100: "hsla(var(--gray-100))", + 150: "hsla(var(--gray-150))", + 200: "hsla(var(--gray-200))", + 300: "hsla(var(--gray-300))", + 400: "hsla(var(--gray-400))", + 500: "hsla(var(--gray-500))", + 600: "hsla(var(--gray-600))", + 700: "hsla(var(--gray-700))", + 800: "hsla(var(--gray-800))", + 900: "hsla(var(--gray-900))", + }, + + purple: { + 50: "hsla(var(--purple-50))", + 100: "hsla(var(--purple-100))", + 200: "hsla(var(--purple-200))", + 300: "hsla(var(--purple-300))", + 400: "hsla(var(--purple-400))", + 500: "hsla(var(--purple-500))", + 600: "hsla(var(--purple-600))", + 700: "hsla(var(--purple-700))", + 800: "hsla(var(--purple-800))", + 900: "hsla(var(--purple-900))", }, blue: { - 50: "var(--blue-50)", - 100: "var(--blue-100)", - 200: "var(--blue-200)", - 300: "var(--blue-300)", - 400: "var(--blue-400)", - 500: "var(--blue-500)", - 600: "var(--blue-600)", - 700: "var(--blue-700)", - 800: "var(--blue-800)", - 900: "var(--blue-900)", + 50: "hsla(var(--blue-50))", + 100: "hsla(var(--blue-100))", + 200: "hsla(var(--blue-200))", + 300: "hsla(var(--blue-300))", + 400: "hsla(var(--blue-400))", + 500: "hsla(var(--blue-500))", + 600: "hsla(var(--blue-600))", + 700: "hsla(var(--blue-700))", + 800: "hsla(var(--blue-800))", + 900: "hsla(var(--blue-900))", }, orange: { - 50: "var(--orange-50)", - 100: "var(--orange-100)", - 200: "var(--orange-200)", - 300: "var(--orange-300)", - 400: "var(--orange-400)", - 500: "var(--orange-500)", - 550: "var(--orange-550)", - 600: "var(--orange-600)", - 700: "var(--orange-700)", - 800: "var(--orange-800)", - 900: "var(--orange-900)", + 50: "hsla(var(--orange-50))", + 100: "hsla(var(--orange-100))", + 200: "hsla(var(--orange-200))", + 300: "hsla(var(--orange-300))", + 400: "hsla(var(--orange-400))", + 500: "hsla(var(--orange-500))", + 550: "hsla(var(--orange-550))", + 600: "hsla(var(--orange-600))", + 700: "hsla(var(--orange-700))", + 800: "hsla(var(--orange-800))", + 900: "hsla(var(--orange-900))", }, - primary: { - DEFAULT: "var(--primary)", - "high-contrast": "var(--primary-high-contrast)", - "low-contrast": "var(--primary-low-contrast)", - hover: "var(--primary-hover)", - visited: "var(--primary-visited)", - light: "var(--primary-light)", - dark: "var(--primary-dark)", - pressed: "var(--primary-pressed)", + DEFAULT: "hsla(var(--primary))", + "high-contrast": "hsla(var(--primary-high-contrast))", + "low-contrast": "hsla(var(--primary-low-contrast))", + hover: "hsla(var(--primary-hover))", + visited: "hsla(var(--primary-visited))", + action: "hsla(var(--primary-action))", + "action-hover": "hsla(var(--primary-action-hover))", + light: "hsla(var(--primary-light))" /* TODO: Migrate/deprecate */, + dark: "hsla(var(--primary-dark))" /* TODO: Migrate/deprecate */, + }, + accent: { + a: { + DEFAULT: "hsla(var(--accent-a))", + hover: "hsla(var(--accent-a-hover))", + }, + b: { + DEFAULT: "hsla(var(--accent-b))", + hover: "hsla(var(--accent-b-hover))", + }, + c: { + DEFAULT: "hsla(var(--accent-c))", + hover: "hsla(var(--accent-c-hover))", + }, }, body: { - DEFAULT: "var(--body)", - medium: "var(--body-medium)", - light: "var(--body-light)", - inverted: "var(--body-inverted)", + DEFAULT: "hsla(var(--body))", + medium: "hsla(var(--body-medium))", + light: "hsla(var(--body-light))", }, background: { - DEFAULT: "var(--background)", - highlight: "var(--background-highlight)", + DEFAULT: "hsla(var(--background))", + highlight: "hsla(var(--background-highlight))", }, - disabled: "var(--disabled)", - neutral: "var(--neutral)", + /** @deprecated */ + neutral: "hsla(var(--neutral))", // TODO: Migrate + /** @deprecated */ + "switch-background": "hsla(var(--switch-background))", // TODO: Migrate + disabled: "hsla(var(--disabled))", "tooltip-shadow": "var(--tooltip-shadow)", - "switch-background": "var(--switch-background)", "hub-hero-content-bg": "var(--hub-hero-content-bg)", "search-background": "var(--search-background)", attention: { - DEFAULT: "var(--attention)", - light: "var(--attention-light)", - outline: "var(--attention-outline)", + DEFAULT: "hsla(var(--attention))", + light: "hsla(var(--attention-light))", + outline: "hsla(var(--attention-outline))", }, error: { - DEFAULT: "var(--error)", - light: "var(--error-light)", - outline: "var(--error-outline)", - neutral: "var(--error-neutral)", + DEFAULT: "hsla(var(--error))", + light: "hsla(var(--error-light))", + outline: "hsla(var(--error-outline))", + neutral: "hsla(var(--error-neutral))", }, success: { - DEFAULT: "var(--success)", - light: "var(--success-light)", - outline: "var(--success-outline)", - neutral: "var(--success-neutral)", + DEFAULT: "hsla(var(--success))", + light: "hsla(var(--success-light))", + outline: "hsla(var(--success-outline))", + neutral: "hsla(var(--success-neutral))", }, }, backgroundImage: { - "main-gradient": "var(--main-gradient)", + "gradient-main": "var(--gradient-main)", + "gradient-banner": "var(--gradient-banner)", + "main-gradient": "var(--gradient-main)", // TODO: Duplicate; remove one "feedback-gradient": "var(--feedback-gradient)", "banner-grid-gradient": "var(--banner-grid-gradient)", + "radial-a": "var(--radial-a)", }, boxShadow: { "table-box": "var(--table-box-shadow)", - table: - "0 14px 66px rgba(0,0,0,.07), 0 10px 17px rgba(0,0,0,.03), 0 4px 7px rgba(0,0,0,.05)", + table: ` + 0 14px 66px rgba(0,0,0,.07), + 0 10px 17px rgba(0,0,0,.03), + 0 4px 7px rgba(0,0,0,.05)`, drop: "0 4px 17px 0 rgba(0,0,0,0.08)", "table-box-hover": "0px 8px 17px rgba(0, 0, 0, 0.15)", "table-item-box": "var(--table-item-box-shadow)", - "table-item-box-hover": "0 0 1px var(--primary)", + "table-item-box-hover": "0 0 1px hsla(var(--primary))", "grid-yellow-box-shadow": "8px 8px 0px 0px #ffe78e", "grid-blue-box-shadow": "8px 8px 0px 0px #a7d0f4", // Part of new DS - "menu-accordion": - "0px 2px 2px 0px rgba(0, 0, 0, 0.12) inset, 0px -3px 2px 0px rgba(0, 0, 0, 0.14) inset", + "menu-accordion": ` + 0px 2px 2px 0px rgba(0, 0, 0, 0.12) inset, + 0px -3px 2px 0px rgba(0, 0, 0, 0.14) inset`, // TODO: From current theme. Deprecate for 'button-hover' - primary: "4px 4px 0px 0px var(--primary)", - "button-hover": "4px 4px 0 0 var(--primary-low-contrast)", + primary: "4px 4px 0px 0px hsla(var(--primary))", + "button-hover": "4px 4px 0 0 hsla(var(--primary-low-contrast))", tooltip: "0 0 16px var(--tooltip-shadow)", + "svg-button-link": ` + var(--shadow-svg-button-link-1), var(--shadow-svg-button-link-2), + var(--shadow-svg-button-link-3), var(--shadow-svg-button-link-4)`, + "svg-button-link-hover": ` + var(--shadow-svg-button-link-1-hover), + var(--shadow-svg-button-link-2-hover), + var(--shadow-svg-button-link-3-hover), + var(--shadow-svg-button-link-4-hover)`, + "card-hover": "var(--shadow-body-md), var(--shadow-body-lg)", + "window-box": ` + var(--shadow-window-box-1), var(--shadow-window-box-2), + var(--shadow-window-box-3), var(--shadow-window-box-4), + var(--shadow-window-box-5)`, }, spacing: { 7.5: "1.875rem", 10.5: "2.625rem", 19: "4.75rem", // Nav height 31: "7.75rem", // FeedbackWidget conditional bottom offset + 128: "32rem", }, keyframes: { "accordion-down": { @@ -185,6 +235,13 @@ const config = { "accordion-down": "accordion-down 0.2s ease-out", "accordion-up": "accordion-up 0.2s ease-out", }, + // Add custom border-radius tailwinds extension for "4xl" as "2rem" + borderRadius: { + "4xl": "2rem" /* 32px */, + }, + gridTemplateColumns: { + bento: "2rem repeat(10, 1fr) 2rem", + }, textUnderlineOffset: { 3: "3px", }, diff --git a/yarn.lock b/yarn.lock index 2e50d5aeec9..3b7c075e1a8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4501,6 +4501,24 @@ "@radix-ui/react-use-previous" "1.1.0" "@radix-ui/react-use-size" "1.1.0" +"@radix-ui/react-tooltip@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.1.2.tgz#c42db2ffd7dcc6ff3d65407c8cb70490288f518d" + integrity sha512-9XRsLwe6Yb9B/tlnYCPVUd/TFS4J7HuOZW345DCeC6vKIxQGMZdx21RK4VoZauPD5frgkXTYVS5y90L+3YBn4w== + dependencies: + "@radix-ui/primitive" "1.1.0" + "@radix-ui/react-compose-refs" "1.1.0" + "@radix-ui/react-context" "1.1.0" + "@radix-ui/react-dismissable-layer" "1.1.0" + "@radix-ui/react-id" "1.1.0" + "@radix-ui/react-popper" "1.2.0" + "@radix-ui/react-portal" "1.1.1" + "@radix-ui/react-presence" "1.1.0" + "@radix-ui/react-primitive" "2.0.0" + "@radix-ui/react-slot" "1.1.0" + "@radix-ui/react-use-controllable-state" "1.1.0" + "@radix-ui/react-visually-hidden" "1.1.0" + "@radix-ui/react-use-callback-ref@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz#f4bb1f27f2023c984e6534317ebc411fc181107a" @@ -5902,22 +5920,6 @@ resolved "https://registry.yarnpkg.com/@types/escodegen/-/escodegen-0.0.6.tgz#5230a9ce796e042cda6f086dbf19f22ea330659c" integrity sha512-AjwI4MvWx3HAOaZqYsjKWyEObT9lcVV0Y0V8nXo6cXzN8ZiMxVhf6F3d/UNvXVGKrEzL/Dluc5p+y9GkzlTWig== -"@types/eslint-scope@^3.7.3": - version "3.7.7" - resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" - integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== - dependencies: - "@types/eslint" "*" - "@types/estree" "*" - -"@types/eslint@*": - version "8.56.10" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.10.tgz#eb2370a73bf04a901eeba8f22595c7ee0f7eb58d" - integrity sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ== - dependencies: - "@types/estree" "*" - "@types/json-schema" "*" - "@types/estree@*", "@types/estree@^1.0.0", "@types/estree@^1.0.5": version "1.0.5" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" @@ -5980,7 +5982,7 @@ resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== -"@types/json-schema@*", "@types/json-schema@^7.0.12", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": +"@types/json-schema@^7.0.12", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== @@ -6199,6 +6201,13 @@ resolved "https://registry.yarnpkg.com/@types/shimmer/-/shimmer-1.2.0.tgz#9b706af96fa06416828842397a70dfbbf1c14ded" integrity sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg== +"@types/swiper@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@types/swiper/-/swiper-6.0.0.tgz#9934ecd569611b660a2a9bf200f25ce5ba4b4d63" + integrity sha512-QPZRgxZ+ivXXtzV43B3LxpXUIC7FE/EoKM+rtxngmgt2M7eeUYypZhyqZD8UxJtlBcUDw/ATGoVeSNpvBBrz2w== + dependencies: + swiper "*" + "@types/ungap__structured-clone@^0.3.0": version "0.3.3" resolved "https://registry.yarnpkg.com/@types/ungap__structured-clone/-/ungap__structured-clone-0.3.3.tgz#cf7e1252f18f5ee39291a8f52fa83c31b0102fc6" @@ -6219,6 +6228,13 @@ resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.8.tgz#7545ba4fc3c003d6c756f651f3bf163d8f0f29ba" integrity sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA== +"@types/xml2js@^0.4.14": + version "0.4.14" + resolved "https://registry.yarnpkg.com/@types/xml2js/-/xml2js-0.4.14.tgz#5d462a2a7330345e2309c6b549a183a376de8f9a" + integrity sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ== + dependencies: + "@types/node" "*" + "@typescript-eslint/eslint-plugin@^6.19.0": version "6.21.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz#30830c1ca81fd5f3c2714e524c4303e0194f9cd3" @@ -7287,7 +7303,7 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@^3.0.2, braces@^3.0.3, braces@~3.0.2: +braces@^3.0.3, braces@~3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== @@ -8812,10 +8828,10 @@ enhanced-resolve@^5.12.0, enhanced-resolve@^5.7.0: graceful-fs "^4.2.4" tapable "^2.2.0" -enhanced-resolve@^5.17.0: - version "5.17.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz#d037603789dd9555b89aaec7eb78845c49089bc5" - integrity sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA== +enhanced-resolve@^5.17.1: + version "5.17.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" + integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -11334,11 +11350,6 @@ lru-cache@^6.0.0: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.0.tgz#0bd445ca57363465900f4d1f9bd8db343a4d95c3" integrity sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q== -lucide-react@^0.400.0: - version "0.400.0" - resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.400.0.tgz#8dc044bc1ace05fde5bdd4a8a7ad35c9e69ca575" - integrity sha512-rpp7pFHh3Xd93KHixNgB0SqThMHpYNzsGUu69UaQbSZ75Q/J3m5t6EhKyMT3m4w2WOxmJ2mY0tD3vebnXqQryQ== - lz-string@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" @@ -11916,22 +11927,14 @@ micromark@^3.0.0: micromark-util-types "^1.0.1" uvu "^0.5.0" -micromatch@^4.0.2, micromatch@^4.0.5, micromatch@~4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.7.tgz#33e8190d9fe474a9895525f5618eee136d46c2e5" - integrity sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q== +micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5, micromatch@~4.0.7: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== dependencies: braces "^3.0.3" picomatch "^2.3.1" -micromatch@^4.0.4: - version "4.0.5" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" - integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== - dependencies: - braces "^3.0.2" - picomatch "^2.3.1" - miller-rabin@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" @@ -13084,7 +13087,7 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -"prettier-fallback@npm:prettier@^3": +"prettier-fallback@npm:prettier@^3", prettier@^3.1.1: version "3.3.2" resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.2.tgz#03ff86dc7c835f2d2559ee76876a3914cec4a90a" integrity sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA== @@ -13099,11 +13102,6 @@ prettier@^2.0.5, prettier@^2.8.8: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== -prettier@^3.1.1: - version "3.3.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.2.tgz#03ff86dc7c835f2d2559ee76876a3914cec4a90a" - integrity sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA== - prettier@^3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.3.tgz#30c54fe0be0d8d12e6ae61dbb10109ea00d53105" @@ -14015,6 +14013,11 @@ sass-loader@^12.4.0: klona "^2.0.4" neo-async "^2.6.2" +sax@>=0.6.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" + integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== + scheduler@^0.23.0: version "0.23.0" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" @@ -14713,6 +14716,16 @@ svgo@^3.0.2: csso "^5.0.5" picocolors "^1.0.0" +swiper@*: + version "11.1.9" + resolved "https://registry.yarnpkg.com/swiper/-/swiper-11.1.9.tgz#55505c7cf4723b678df8220fc06152b793585dbc" + integrity sha512-rflu8zvfGa3x1v/aeSufk4zRJffhOQowyvtJlp46sUBnOqAuk1Rdv5Ldj0AWWBV595iZ+ZMk7VB35ZRtRUomtA== + +swiper@^11.1.10: + version "11.1.10" + resolved "https://registry.yarnpkg.com/swiper/-/swiper-11.1.10.tgz#4d3df50ff8afc4960e9644ed6e5828d35ab38853" + integrity sha512-pAVM6vCb6bumj2B9aSh67l3wP1j5YR8dPQM1YhQKMpnBc33vs+RpyVz6NZYZl/ZopCBSYbbWK5nvESwbmU0QXQ== + tailwind-merge@^2.3.0: version "2.4.0" resolved "https://registry.yarnpkg.com/tailwind-merge/-/tailwind-merge-2.4.0.tgz#1345209dc1f484f15159c9180610130587703042" @@ -15700,11 +15713,10 @@ webpack-virtual-modules@^0.6.1: integrity sha512-poXpCylU7ExuvZK8z+On3kX+S8o/2dQ/SVYueKA0D4WEMXROXgY8Ez50/bQEUmvoSMMrWcrJqCHuhAbsiwg7Dg== webpack@5: - version "5.92.1" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.92.1.tgz#eca5c1725b9e189cffbd86e8b6c3c7400efc5788" - integrity sha512-JECQ7IwJb+7fgUFBlrJzbyu3GEuNBcdqr1LD7IbSzwkSmIevTm8PF+wej3Oxuz/JFBUZ6O1o43zsPkwm1C4TmA== + version "5.94.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.94.0.tgz#77a6089c716e7ab90c1c67574a28da518a20970f" + integrity sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg== dependencies: - "@types/eslint-scope" "^3.7.3" "@types/estree" "^1.0.5" "@webassemblyjs/ast" "^1.12.1" "@webassemblyjs/wasm-edit" "^1.12.1" @@ -15713,7 +15725,7 @@ webpack@5: acorn-import-attributes "^1.9.5" browserslist "^4.21.10" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.17.0" + enhanced-resolve "^5.17.1" es-module-lexer "^1.2.1" eslint-scope "5.1.1" events "^3.2.0" @@ -15841,6 +15853,19 @@ ws@^8.2.3: resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== +xml2js@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.6.2.tgz#dd0b630083aa09c161e25a4d0901e2b2a929b499" + integrity sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA== + dependencies: + sax ">=0.6.0" + xmlbuilder "~11.0.0" + +xmlbuilder@~11.0.0: + version "11.0.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" + integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== + xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"