Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
365 commits
Select commit Hold shift + click to select a range
a1ee9c4
i18n(ta): LLM translation
wackerow Jul 10, 2026
295ce6f
i18n(te): LLM translation
wackerow Jul 10, 2026
6e32c68
i18n(tr): LLM translation
wackerow Jul 10, 2026
ef972bb
i18n(uk): LLM translation
wackerow Jul 10, 2026
bf63f66
i18n(ur): LLM translation
wackerow Jul 10, 2026
45eb176
i18n(vi): LLM translation
wackerow Jul 10, 2026
2b0b796
i18n(zh): LLM translation
wackerow Jul 10, 2026
bde843d
i18n(zh-tw): LLM translation
wackerow Jul 10, 2026
7d1caeb
i18n: merge tmp-intl/run-0710-0459 into intl/pending-use-translations…
wackerow Jul 10, 2026
a8d2c4c
Merge pull request #18752 from ethereum/intl/pending-use-translations…
myelinated-wackerow Jul 10, 2026
ef693b3
refactor: migrate site shell to useTranslations
myelinated-wackerow Jul 10, 2026
a61665e
chore: retrigger deploy preview
myelinated-wackerow Jul 10, 2026
aa27f86
fix: restore use client directives to first line
myelinated-wackerow Jul 10, 2026
b98b5fa
refactor: migrate table components to useTranslations
myelinated-wackerow Jul 10, 2026
780bc22
refactor: migrate misc component i18n hooks
myelinated-wackerow Jul 10, 2026
6624032
refactor: migrate colon-form i18n call sites
myelinated-wackerow Jul 10, 2026
3605b8e
chore: remove orphaned WhatTabs and WhySwiper
myelinated-wackerow Jul 10, 2026
81027a1
refactor: migrate app-directory i18n hooks
myelinated-wackerow Jul 10, 2026
306410f
fix: strip rogue anchor from get-eth description i18n
myelinated-wackerow Jul 10, 2026
d4beeab
ci: pin paths-filter to commit SHA (v4.0.2)
pettinarip Jul 10, 2026
4d0a681
Merge pull request #18744 from ethereum/md-video-ratio
pettinarip Jul 10, 2026
1032792
Merge pull request #18724 from ethereum/intl/pending-simulator
pettinarip Jul 10, 2026
4f5b0e2
Merge pull request #18715 from ethereum/fix/find-wallet-filter-ui
pettinarip Jul 10, 2026
c15f000
Merge pull request #18723 from ethereum/simulator
pettinarip Jul 10, 2026
429490e
Merge pull request #18725 from ethereum/apps-subcategory
pettinarip Jul 10, 2026
659c90e
Replace Blobscan with growthepie as blob stats data source
pettinarip Jul 10, 2026
a198bad
Update src/data-layer/tasks.ts
pettinarip Jul 10, 2026
cb762a0
Rename storage key to fetch-blob-stats
pettinarip Jul 10, 2026
2e3d7e1
fix(simulator): modal not closing on direct load
myelinated-wackerow Jul 10, 2026
01ee187
Merge pull request #18703 from ethereum/report-schema-fix
wackerow Jul 10, 2026
9982d28
Merge pull request #18749 from ethereum/use-translations-pattern
wackerow Jul 10, 2026
ead18f6
Merge pull request #18751 from ethereum/use-translations-quiz
wackerow Jul 10, 2026
4993d28
Merge pull request #18753 from ethereum/use-translations-shell
wackerow Jul 10, 2026
5fa168a
feat: gh-aw intake automation — PR review, triage, and digest workflo…
pettinarip Jul 10, 2026
c70b730
Merge pull request #18754 from ethereum/use-translations-tables
wackerow Jul 10, 2026
bcf0cf5
fix: switch thumb overflows track in RTL
myelinated-wackerow Jul 10, 2026
519ff46
feat: add locale-aware formatDuration helper
myelinated-wackerow Jul 10, 2026
aae4c11
fix: i18n gaps on layer-2 networks page
myelinated-wackerow Jul 10, 2026
20514c7
i18n(ar): LLM translation
wackerow Jul 10, 2026
e263dbd
i18n(bn): LLM translation
wackerow Jul 10, 2026
bff49b3
i18n(cs): LLM translation
wackerow Jul 10, 2026
67217fb
i18n(de): LLM translation
wackerow Jul 10, 2026
541fadf
i18n(es): LLM translation
wackerow Jul 10, 2026
5cab35d
i18n(fr): LLM translation
wackerow Jul 10, 2026
b149e05
i18n(hi): LLM translation
wackerow Jul 10, 2026
1104131
i18n(id): LLM translation
wackerow Jul 10, 2026
656d549
i18n(it): LLM translation
wackerow Jul 10, 2026
b7b74c9
i18n(ja): LLM translation
wackerow Jul 10, 2026
43146af
i18n(ko): LLM translation
wackerow Jul 10, 2026
22a5bf5
i18n(mr): LLM translation
wackerow Jul 10, 2026
5099162
i18n(pl): LLM translation
wackerow Jul 10, 2026
7c86381
i18n(pt-br): LLM translation
wackerow Jul 10, 2026
22e6440
i18n(ru): LLM translation
wackerow Jul 10, 2026
13b7cc6
i18n(sw): LLM translation
wackerow Jul 10, 2026
0ee82e2
i18n(ta): LLM translation
wackerow Jul 10, 2026
4003568
i18n(te): LLM translation
wackerow Jul 10, 2026
da35095
i18n(tr): LLM translation
wackerow Jul 10, 2026
ae4f8b3
i18n(uk): LLM translation
wackerow Jul 10, 2026
11e24ce
i18n(ur): LLM translation
wackerow Jul 10, 2026
b5dabbe
i18n(vi): LLM translation
wackerow Jul 10, 2026
e8878a9
i18n(zh): LLM translation
wackerow Jul 10, 2026
cb87e8d
i18n(zh-tw): LLM translation
wackerow Jul 10, 2026
03ba87a
i18n: merge tmp-intl/run-0710-1859 into intl/pending-fix-rtl-l2-networks
wackerow Jul 10, 2026
11bc60e
refactor: rebuild /assets page on design system
myelinated-wackerow Jul 10, 2026
f84ad9d
Merge pull request #18755 from ethereum/use-translations-singles
wackerow Jul 10, 2026
483bc84
Merge pull request #18756 from ethereum/use-translations-nav-merge
wackerow Jul 10, 2026
7db27d7
chore: suppress mount-only exhaustive-deps warning
myelinated-wackerow Jul 10, 2026
1b42794
refactor: translate community stories via intl
myelinated-wackerow Jul 10, 2026
8fb582e
Merge pull request #18757 from ethereum/use-translations-app
wackerow Jul 10, 2026
5941ccf
style: polish asset card presentation
myelinated-wackerow Jul 10, 2026
7467ca6
i18n(ar): LLM translation
wackerow Jul 10, 2026
7a670cb
i18n(bn): LLM translation
wackerow Jul 10, 2026
b578eab
i18n(cs): LLM translation
wackerow Jul 10, 2026
411e5a5
i18n(de): LLM translation
wackerow Jul 10, 2026
705297c
i18n(es): LLM translation
wackerow Jul 10, 2026
bbef097
i18n(fr): LLM translation
wackerow Jul 10, 2026
25123f5
i18n(hi): LLM translation
wackerow Jul 10, 2026
09eb31a
i18n(id): LLM translation
wackerow Jul 10, 2026
efe22f9
i18n(it): LLM translation
wackerow Jul 10, 2026
9db3760
i18n(ja): LLM translation
wackerow Jul 10, 2026
12450d9
i18n(ko): LLM translation
wackerow Jul 10, 2026
5f43c21
i18n(mr): LLM translation
wackerow Jul 10, 2026
d90d2a4
i18n(pl): LLM translation
wackerow Jul 10, 2026
cf3cd8b
i18n(pt-br): LLM translation
wackerow Jul 10, 2026
1674ceb
i18n(ru): LLM translation
wackerow Jul 10, 2026
0b43606
i18n(sw): LLM translation
wackerow Jul 10, 2026
177e00f
i18n(ta): LLM translation
wackerow Jul 10, 2026
fd1860e
i18n(te): LLM translation
wackerow Jul 10, 2026
94f5c2a
i18n(tr): LLM translation
wackerow Jul 10, 2026
4a7be5a
i18n(uk): LLM translation
wackerow Jul 10, 2026
47ff830
i18n(ur): LLM translation
wackerow Jul 10, 2026
eb8297d
i18n(vi): LLM translation
wackerow Jul 10, 2026
f64878f
i18n(zh): LLM translation
wackerow Jul 10, 2026
0a33bdb
i18n(zh-tw): LLM translation
wackerow Jul 10, 2026
f6da802
i18n: merge tmp-intl/run-0710-2059 into intl/pending-dev
wackerow Jul 10, 2026
6526455
feat: show story original when locale matches
myelinated-wackerow Jul 10, 2026
50aebbe
docs(intl-review): add PR 18767 review findings
myelinated-wackerow Jul 10, 2026
354f382
refactor: polish FeedbackWidget popup UI
myelinated-wackerow Jul 10, 2026
eddbbb8
Merge remote-tracking branch 'origin/dev' into assets-page-refactor
myelinated-wackerow Jul 10, 2026
c1d0adc
Merge pull request #18767 from ethereum/intl/pending-dev
wackerow Jul 10, 2026
d71a395
docs(intl-review): add PR 18765 review findings
myelinated-wackerow Jul 10, 2026
fc88591
Merge pull request #18765 from ethereum/intl/pending-fix-rtl-l2-networks
wackerow Jul 10, 2026
04f10b3
refactor: translate what-are-apps stories
myelinated-wackerow Jul 10, 2026
f114f0d
refactor: make Translation self-contained
myelinated-wackerow Jul 10, 2026
8c564a5
i18n(ar): LLM translation
wackerow Jul 10, 2026
997dd36
i18n(bn): LLM translation
wackerow Jul 10, 2026
d1732d0
i18n(cs): LLM translation
wackerow Jul 10, 2026
f077ec5
i18n(de): LLM translation
wackerow Jul 10, 2026
5156d18
i18n(es): LLM translation
wackerow Jul 10, 2026
b9e38a5
i18n(fr): LLM translation
wackerow Jul 10, 2026
8a2f095
i18n(hi): LLM translation
wackerow Jul 10, 2026
62225b2
i18n(id): LLM translation
wackerow Jul 10, 2026
c12f617
i18n(it): LLM translation
wackerow Jul 10, 2026
e64e97c
i18n(ja): LLM translation
wackerow Jul 10, 2026
4e628ed
i18n(ko): LLM translation
wackerow Jul 10, 2026
4a7d16d
i18n(mr): LLM translation
wackerow Jul 10, 2026
60646ce
i18n(pl): LLM translation
wackerow Jul 10, 2026
8733b3f
i18n(pt-br): LLM translation
wackerow Jul 10, 2026
306ff76
i18n(ru): LLM translation
wackerow Jul 10, 2026
70d1291
i18n(sw): LLM translation
wackerow Jul 10, 2026
9ef2919
i18n(ta): LLM translation
wackerow Jul 10, 2026
643e9c9
i18n(te): LLM translation
wackerow Jul 10, 2026
46188ab
i18n(tr): LLM translation
wackerow Jul 10, 2026
961fffa
i18n(uk): LLM translation
wackerow Jul 10, 2026
044f5d1
i18n(ur): LLM translation
wackerow Jul 10, 2026
7dbaafe
i18n(vi): LLM translation
wackerow Jul 10, 2026
2acd6a5
i18n(zh): LLM translation
wackerow Jul 10, 2026
721ab99
i18n(zh-tw): LLM translation
wackerow Jul 10, 2026
46904c4
i18n: sanitize translation output
wackerow Jul 10, 2026
945211a
i18n: merge tmp-intl/run-0710-2341 into intl/pending-stories-intl
wackerow Jul 10, 2026
e415151
docs: clarify glossary path in Translation deprecation
myelinated-wackerow Jul 11, 2026
889afd2
fix(i18n): review community stories translations
myelinated-wackerow Jul 11, 2026
8e25023
refactor: story-card strings to own namespace
myelinated-wackerow Jul 11, 2026
15392ae
Merge remote-tracking branch 'origin/stories-intl' into intl/pending-…
myelinated-wackerow Jul 11, 2026
4e41114
i18n: migrate story-card strings per locale
myelinated-wackerow Jul 11, 2026
9bfbb6e
i18n(ar): LLM translation
wackerow Jul 11, 2026
caa575c
i18n(bn): LLM translation
wackerow Jul 11, 2026
3b590fe
i18n(cs): LLM translation
wackerow Jul 11, 2026
1c2fba1
i18n(de): LLM translation
wackerow Jul 11, 2026
5468d19
i18n(es): LLM translation
wackerow Jul 11, 2026
e219fd8
i18n(fr): LLM translation
wackerow Jul 11, 2026
09d8142
i18n(hi): LLM translation
wackerow Jul 11, 2026
4ec9c19
i18n(id): LLM translation
wackerow Jul 11, 2026
da943b7
i18n(it): LLM translation
wackerow Jul 11, 2026
b177eeb
i18n(ja): LLM translation
wackerow Jul 11, 2026
048bdfd
i18n(ko): LLM translation
wackerow Jul 11, 2026
d4bd1a5
i18n(mr): LLM translation
wackerow Jul 11, 2026
c3b7769
i18n(pl): LLM translation
wackerow Jul 11, 2026
f1eb235
i18n(pt-br): LLM translation
wackerow Jul 11, 2026
12b1931
i18n(ru): LLM translation
wackerow Jul 11, 2026
22674ee
i18n(sw): LLM translation
wackerow Jul 11, 2026
83842dd
i18n(ta): LLM translation
wackerow Jul 11, 2026
072b30e
i18n(te): LLM translation
wackerow Jul 11, 2026
e34b3da
i18n(tr): LLM translation
wackerow Jul 11, 2026
c0a8a9c
i18n(uk): LLM translation
wackerow Jul 11, 2026
5ac1e74
i18n(ur): LLM translation
wackerow Jul 11, 2026
103162b
i18n(vi): LLM translation
wackerow Jul 11, 2026
de79d54
i18n(zh): LLM translation
wackerow Jul 11, 2026
b1960f5
i18n(zh-tw): LLM translation
wackerow Jul 11, 2026
65e01ec
i18n: merge tmp-intl/run-0711-1800 into intl/pending-stories-intl
wackerow Jul 11, 2026
775d686
refactor: move swiper a11y keys to own namespace
myelinated-wackerow Jul 11, 2026
fb6e3e2
fix(ui/intl): overflow handling for long intl strings
myelinated-wackerow Jul 11, 2026
fa28f52
fix: sweep 5 PRs per run, allow correcting stale type labels
pettinarip Jul 13, 2026
490a42a
Merge pull request #18690 from ethereum/ci/chromatic-dev-prs
wackerow Jul 13, 2026
f793819
Merge pull request #18746 from ethereum/modal-backdrop-dark
pettinarip Jul 13, 2026
1a54768
Merge pull request #18766 from ethereum/assets-page-refactor
pettinarip Jul 13, 2026
08f2066
Merge pull request #18769 from ethereum/feedback-widget-popup
pettinarip Jul 13, 2026
ff549ae
Merge pull request #18761 from ethereum/fix/simulator-modal-close
pettinarip Jul 13, 2026
204f175
Merge pull request #18745 from ethereum/dropdown-subitem-dash
pettinarip Jul 13, 2026
91b8dc6
Merge pull request #18771 from ethereum/use-translations-finale
pettinarip Jul 13, 2026
a95241d
Merge pull request #18774 from ethereum/fix/eth-price-card-wrap
pettinarip Jul 13, 2026
648e319
patch: update variant comment and jsonld hero image
myelinated-wackerow Jul 13, 2026
43c04f6
Merge pull request #18716 from ethereum/privacy-page
wackerow Jul 13, 2026
b985ac9
Merge pull request #18739 from ethereum/intl/pending-privacy-page
wackerow Jul 13, 2026
c8d793e
Merge branch 'dev' into fix/rtl-l2-networks
myelinated-wackerow Jul 13, 2026
9e2acee
Merge pull request #18764 from ethereum/fix/rtl-l2-networks
wackerow Jul 13, 2026
104bf48
docs: add ethrex to the list of execution clients
MegaRedHand Jul 13, 2026
5772c22
feat(ui/card): variant-aware link hover
myelinated-wackerow Jul 13, 2026
e9c75b6
fix(cards): banner padding and 1rem gaps
myelinated-wackerow Jul 13, 2026
0a01d44
docs(ui/card): link hover + banner padding rule
myelinated-wackerow Jul 13, 2026
7a51206
Merge pull request #18759 from ethereum/worktree-blob-stats-growthepie
pettinarip Jul 14, 2026
33cb320
fix: add untrusted-data guardrail to team-digest prompt
pettinarip Jul 14, 2026
77418ff
Merge pull request #18737 from ethereum/feat/gh-aw-intake-automation
pettinarip Jul 14, 2026
74308c3
docs(ui/card): link/action interaction patterns
myelinated-wackerow Jul 14, 2026
20f0e0b
fix(i18n): disambiguate zh agent-tooling label
myelinated-wackerow Jul 14, 2026
ba30cf8
i18n(ar): LLM translation
wackerow Jul 14, 2026
0660087
i18n(bn): LLM translation
wackerow Jul 14, 2026
bf6abd0
i18n(cs): LLM translation
wackerow Jul 14, 2026
dc7371a
i18n(de): LLM translation
wackerow Jul 14, 2026
8d2f73c
i18n(es): LLM translation
wackerow Jul 14, 2026
958c434
i18n(fr): LLM translation
wackerow Jul 14, 2026
d40c83a
i18n(hi): LLM translation
wackerow Jul 14, 2026
690c305
i18n(id): LLM translation
wackerow Jul 14, 2026
54a32c0
i18n(it): LLM translation
wackerow Jul 14, 2026
380b721
i18n(ja): LLM translation
wackerow Jul 14, 2026
938a30d
i18n(ko): LLM translation
wackerow Jul 14, 2026
56cd658
i18n(mr): LLM translation
wackerow Jul 14, 2026
d2814cc
i18n(pl): LLM translation
wackerow Jul 14, 2026
f319103
i18n(pt-br): LLM translation
wackerow Jul 14, 2026
a37b5e9
i18n(ru): LLM translation
wackerow Jul 14, 2026
4e22aeb
i18n(sw): LLM translation
wackerow Jul 14, 2026
272cd56
i18n(ta): LLM translation
wackerow Jul 14, 2026
677c29d
i18n(te): LLM translation
wackerow Jul 14, 2026
df82a61
i18n(tr): LLM translation
wackerow Jul 14, 2026
7ba00a8
i18n(uk): LLM translation
wackerow Jul 14, 2026
9d46143
i18n(ur): LLM translation
wackerow Jul 14, 2026
a695fb9
i18n(vi): LLM translation
wackerow Jul 14, 2026
b5fad12
i18n(zh): LLM translation
wackerow Jul 14, 2026
fe1332e
i18n(zh-tw): LLM translation
wackerow Jul 14, 2026
7c904aa
i18n: merge tmp-intl/run-0714-1732 into intl/pending-fix-swiper-a11y-…
wackerow Jul 14, 2026
67dd739
fix: harden community-stories locale fallback
myelinated-wackerow Jul 14, 2026
e8240c3
Merge remote-tracking branch 'origin/dev' into fix/swiper-a11y-i18n-keys
myelinated-wackerow Jul 14, 2026
a42a722
docs: note Swiper i18n namespace requirement
myelinated-wackerow Jul 14, 2026
da7de56
Merge pull request #18698 from ethereum/fix/swiper-a11y-i18n-keys
wackerow Jul 14, 2026
1473dc4
Merge pull request #18799 from ethereum/intl/pending-fix-swiper-a11y-…
wackerow Jul 14, 2026
802c7be
Merge origin/dev into stories-intl
myelinated-wackerow Jul 14, 2026
ec40cb6
Merge origin/stories-intl into intl/pending-stories-intl
myelinated-wackerow Jul 14, 2026
c274c13
ci(aw): stop agentic workflows posting no-op runs to tracking issue
pettinarip Jul 15, 2026
7b3f3b9
Merge pull request #18805 from ethereum/worktree-stop-aw-noop-issues
pettinarip Jul 15, 2026
fdcdd1a
fix(ui): ui/card border variant
myelinated-wackerow Jul 15, 2026
999fbbd
Merge branch 'dev' into ui-card-outline
myelinated-wackerow Jul 15, 2026
12bf781
Merge pull request #18770 from ethereum/stories-intl
wackerow Jul 15, 2026
0a856f6
Merge pull request #18772 from ethereum/intl/pending-stories-intl
wackerow Jul 15, 2026
b0c749f
Merge pull request #18784 from ethereum/ui-card-outline
wackerow Jul 15, 2026
3be76cb
[recovery] fix: call setRequestLocale before next-intl APIs in video …
pettinarip Jul 13, 2026
c23a377
[recovery] fix: setRequestLocale ordering in resources page (static→d…
pettinarip Jul 14, 2026
f529f47
[recovery] fix: setRequestLocale ordering in wallets page (static→dyn…
pettinarip Jul 14, 2026
dc9c0cd
[recovery] fix: setRequestLocale ordering in [...slug] generateMetadata
pettinarip Jul 14, 2026
e663237
Merge pull request #18734 from ethereum/fix/carousel-rtl-support
wackerow Jul 15, 2026
969ebd3
refactor: remove legacy useTranslation hook (closes #18742)
myelinated-wackerow Jul 15, 2026
4efa4c8
update: token standard further reading
myelinated-wackerow Jul 15, 2026
1267bfc
Merge pull request #18030 from anettrolikova/patch-3
wackerow Jul 15, 2026
8361ac1
Merge pull request #18782 from lambdaclass/add-ethrex-execution-client
wackerow Jul 16, 2026
6df7a46
docs: update README.md [skip ci]
allcontributors[bot] Jul 16, 2026
3a9c041
docs: update .all-contributorsrc [skip ci]
allcontributors[bot] Jul 16, 2026
eca3652
Merge pull request #18816 from ethereum/all-contributors/add-MegaRedHand
wackerow Jul 16, 2026
04f7339
fix: setRequestLocale before next-intl APIs in all generateMetadata
pettinarip Jul 15, 2026
77d47d9
Merge pull request #18800 from ethereum/recovery/fix/slug-catchall-se…
pettinarip Jul 16, 2026
e7fe982
Merge pull request #18798 from ethereum/recovery/fix/resources-setreq…
pettinarip Jul 16, 2026
84cdf6b
Merge pull request #18797 from ethereum/recovery/fix/wallets-setreque…
pettinarip Jul 16, 2026
7e9c9d2
Merge pull request #18785 from ethereum/recovery/fix/videos-slug-setr…
pettinarip Jul 16, 2026
e65cf0e
Merge remote-tracking branch 'origin/dev' into HEAD
pettinarip Jul 16, 2026
a7145c3
docs: update best-practices translation example to next-intl
pettinarip Jul 16, 2026
48bf922
Merge pull request #18814 from ethereum/use-translations-finale
pettinarip Jul 16, 2026
3e28611
Merge pull request #18811 from ethereum/fix/setrequestlocale-metadata…
pettinarip Jul 16, 2026
acab75e
Merge staging into dev
github-actions[bot] Jul 16, 2026
fb5a152
11.15.0
github-actions[bot] Jul 16, 2026
b9b78b7
fix: tutorial skill tag rendering raw translation key
pettinarip Jul 16, 2026
fc99718
fix: quizzes JSON-LD leaking raw translation key
pettinarip Jul 16, 2026
280d430
Merge pull request #18823 from ethereum/fix/tutorial-skill-tag-transl…
pettinarip Jul 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
9 changes: 9 additions & 0 deletions .all-contributorsrc
Original file line number Diff line number Diff line change
Expand Up @@ -14369,6 +14369,15 @@
"contributions": [
"tool"
]
},
{
"login": "MegaRedHand",
"name": "Tomás Grüner",
"avatar_url": "https://avatars.githubusercontent.com/u/47506558?v=4",
"profile": "https://github.com/MegaRedHand",
"contributions": [
"tool"
]
}
],
"contributorsPerLine": 7,
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/design-system/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ When the existing primitive doesn't quite fit, the answer is usually "add a vari
2. **No raw color values.** Use semantic tokens (`text-body`, `bg-background`, `border-border`, `text-primary`). Hex literals and `rgb()` calls bypass dark mode.
3. **Prefer adding a variant** to an existing primitive over creating a new component. Card, Button, Alert, Tag are the most common targets.
4. **Server Components by default.** Only `"use client"` when you need state, effects, browser APIs, or inline event handlers.
5. **All text is translatable.** `getTranslations` (server) or `useTranslations` (client) from `next-intl`. Never hard-code user-facing English.
5. **All text is translatable.** `getTranslations` from `next-intl/server` (server) or `useTranslations` from `next-intl` (client). One namespace-bound `t` per namespace -- bind a second function (e.g. `const tCommon = useTranslations("common")`) to access another namespace. The legacy `@/hooks/useTranslation` wrapper is deprecated for new code. Never hard-code user-facing English.
6. **Logical CSS for direction.** Use `ms-`/`me-`/`ps-`/`pe-`/`inset-s-`/`inset-e-`/`border-s`/`border-e`/`text-start`/`text-end`. The site supports Arabic and Urdu (RTL). Hard-coded `left-`/`right-`/`ml-`/`mr-`/`pl-`/`pr-` breaks RTL.
7. **Locale-aware formatters.** `numberFormat()` from `@/lib/utils/numbers`, `dateTimeFormat()` from `@/lib/utils/date`. Never `toLocaleString` / `Intl.NumberFormat` directly.
8. **`useRtlFlip()` for directional icons** (right-pointing arrows/chevrons). Or use `ChevronNext`/`ChevronPrev` from `@/components/Chevron`.
Expand Down
78 changes: 64 additions & 14 deletions .claude/skills/design-system/references/card-walkthrough.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,16 @@ Card <- parent wrapper, owns CSS vars + variant
import {
Card,
CardBanner,
CardButtonFake,
CardContent,
CardFooter,
CardHeader,
CardParagraph,
CardTitle,
} from "@/components/ui/card"

<Card href="/articles/proof-of-stake">
{/* single CTA: href (whole card links) + CardButtonFake + hoverLift (carries an action) */}
<Card href="/articles/proof-of-stake" hoverLift>
<CardHeader>
<CardBanner background="accent-a">
<Image src={posIllustration} alt="Stylized validators staking ETH" />
Expand All @@ -43,7 +45,7 @@ import {
<CardParagraph>How Ethereum secures the network.</CardParagraph>
</CardContent>
<CardFooter>
<ButtonLink href="/learn-more">Learn more</ButtonLink>
<CardButtonFake>Learn more</CardButtonFake>
</CardFooter>
</Card>
```
Expand All @@ -52,13 +54,33 @@ import {

When you pass `href`, `Card` automatically wraps in `BaseLink` and adds a `group/link` class so descendants can react to card-level hover/focus. You don't need to nest your own `<a>` for "whole card clickable" cards.

**Two hover signals that can stack -- keep their meanings distinct:**

- **`hoverLift`** (the card raises on hover) means the card **carries an action** -- a CTA button or a text link. Add it to *any* card that holds an action, whether or not the card is also a link.
- An **outline ring** (or, on a `ghost` card, a **`bg-background-highlight` fill**) means the **whole card is the click target**. An `href` `Card` renders this automatically (see Interaction props). It's *additive* to `hoverLift`, not an alternative.

**The number of actions decides the shape. This is the canonical rule -- follow it for every new/refactored card:**

- **One CTA -> the whole card is the link, and it carries an action.** Put `href` on the `Card` (automatic outline/fill), render the footer button as a **`CardButtonFake`** (not a `ButtonLink`/`Button` -- a real interactive `<a>`/`<button>` nested inside the card's own anchor is invalid HTML with broken keyboard/hit-target behavior; `CardButtonFake` is a presentational `<div>` mirror of `Button` via `asChild` that inherits the card's `group/link` hover), **and** add `hoverLift` so the card advertises the action it carries. (See the `InteractionPatterns` story.)
- **Two or more CTAs -> the card is NOT a link, but carries actions.** Multiple independent actions can't collapse into one card-level link, so drop `href`, use real `ButtonLink`s (each its own target), and add `hoverLift`. Reach for `CardFooter buttons="compact"` when they should sit inline.
- **No button, action is an inline link in the copy -> non-link card that carries an action.** Keep the card a plain `<div>` (no `href`), use an `InlineLink` in the copy, and add `hoverLift`. No outline ring, which would falsely imply the entire card is a single link.

(A **pure navigation card** -- an `href` card with no interior button or link, e.g. a media/thumbnail tile -- is the one case that skips `hoverLift`: there's no interior action to advertise, so the automatic outline/fill alone carries the "whole card is clickable" signal.)

**Rule: a link card needs interior padding, and its banner belongs in a `CardHeader`.** Because an `href` card renders a variant-aware hover state (fill or ring) tight to its outer edge, its contents must sit inset from that edge. In practice:

- If a link card contains a `CardBanner`, **wrap it in a `CardHeader` by default** so the `--card-pad` inset keeps the banner clear of the hover treatment and its corners concentric with the card. Don't drop a bare `CardBanner` straight into an `href` `Card`.
- Avoid `size="xs"` (which zeroes `--card-pad`) on a link card unless the layout supplies padding another way.

The sanctioned exceptions are cards that arrange the banner differently and **bake the padding into the banner itself** rather than relying on a `CardHeader` -- e.g. `PathwayCard` (`src/components/cards/pathway-card.tsx`), whose banner sits beside the text in a container-query row layout and carries its own `p-*`. If you're building a new bespoke link-card arrangement, follow that model: inset the banner somehow, don't leave it flush.

## Step 2: Pick the `variant`

| Variant | When to use |
|---|---|
| `base` (default) | Standard card on the default page background. Gives `bg-background-highlight` (grey). |
| `nested` | When the Card sits inside a section that already has a non-default background. Gives `bg-background` (white in light, black in dark) so it visually pops out from the colored container. |
| `ghost` | No background. The Card behaves like an outlined container. `--banner-radius` is automatically widened (since the banner's edge IS the card's edge), so any `CardBanner` matches the outer corner radius. |
| `ghost` | No background -- a transparent, outlined-style container. `--banner-radius` is automatically widened so an edge-to-edge `CardBanner` matches the outer corner radius. As a **link** (`href`), a ghost card fills with `bg-background-highlight` on hover instead of showing an outline ring (see Interaction props). |
| `header-bar` | "Top bar" appearance: only the `CardHeader` region gets the highlight background, the rest is bordered, and the header gets row layout (icon + text) with a bottom border. The variant bakes in all of this — just drop a `CardHeader` inside, no extra props. |

## Step 3: Pick the `size`
Expand All @@ -75,18 +97,23 @@ When you pass `href`, `Card` automatically wraps in `BaseLink` and adds a `group
| `sm` | 10px | 10px | Compact list cards |
| `xs` | 0 | 4px | No padding; use when the banner image needs to extend to all edges of the Card |

### Interaction props: `border`, `hoverLift` (and the automatic `href` hover ring)
### Interaction props: `border`, `hoverLift` (and the automatic `href` hover state)

Two independent booleans layer edge/interaction treatment on top of `variant`/`size` -- they're additive, so combine freely:

| Prop | Effect | When |
|---|---|---|
| `border` | Static hairline edge (`ring ring-border`). It's a `ring`, so it never shifts layout. | A resting outline on `nested`/`ghost` cards that need definition against their background. |
| `hoverLift` | `hover:shadow-md` plus a subtle `hover:scale`. | Cards that hold their own actions instead of being a link themselves: multiple action buttons, or text-link actions in the body/footer. The lift signals "interactive content here" without implying the whole card is clickable. |
| `hoverLift` | `hover:shadow-md` plus a subtle `hover:scale`. | Any card that carries an action: a single CTA (alongside the automatic `href` outline/fill), multiple action buttons, or a text link in the body. The lift signals "interactive content here." Skip it only on a pure navigation card (an `href` tile with no interior button/link), where the outline/fill alone carries the signal. |

**There is no `hoverOutline` prop anymore.** Every Card with an `href` gets a hover affordance automatically, chosen by `variant` -- you don't set it:

- **`ghost` link cards fill on hover** with `bg-background-highlight` and show *no* outline. A ghost card is transparent at rest, so the fill is the affordance; an offset ring would clip against a flush banner and read as noise. This is the treatment for the site's media/link cards (video, hackathon, story, and latest-article grids).
- **`base`, `nested`, and `header-bar` link cards keep the outline ring** (`ring-transparent` at rest -> `ring-primary-hover` on hover). They already sit on a fill, so a hover fill would be invisible; the ring is what reads.

**There is no `hoverOutline` prop anymore.** Every Card with an `href` gets the hover ring automatically (transparent at rest, `ring-primary-hover` on hover) -- it's baked into the link render path, not a variant you set. It also can't be applied to non-link cards by hand; if a card isn't a link, use `hoverLift` (or nothing).
It's implemented with `compoundVariants` keyed on an internal `interactive` flag (set from `href`, omitted from the public `CardProps`) -- not a variant you pass, and not a class you can hand-apply to a non-link card. The outline/fill is additive to `hoverLift`: a single-CTA link card gets both (ring/fill from `href`, lift from `hoverLift`); a non-link card that carries an action gets `hoverLift` alone.

`border` composes with the automatic ring: on an `href` Card, the `ring-border` shows at rest and `hover:ring-primary-hover` takes over on hover, reading as a resting border that brightens to primary. These props replaced the old `hoverEffect="lift"` prop.
`border` composes with the ring on non-ghost link cards: the `ring-border` shows at rest and `hover:ring-primary-hover` takes over, reading as a resting border that brightens to primary. On a `ghost` link card there is no hover ring, so a `border` simply stays as the resting outline while the highlight fills behind it. These props replaced the old `hoverEffect="lift"` prop.

## Step 4: Border Radius "Just Works"

Expand Down Expand Up @@ -130,17 +157,39 @@ When *should* you use `className`? Things that are genuinely outside the Card's
### `CardFooter`

```tsx
{/* single CTA on an href + hoverLift card */}
<CardFooter>
<ButtonLink href="...">CTA</ButtonLink>
<CardButtonFake>CTA</CardButtonFake>
</CardFooter>
{/* two CTAs on a non-link, hoverLift card */}
<CardFooter buttons="compact">
<ButtonLink href="...">Primary</ButtonLink>
<ButtonLink href="..." variant="outline">Secondary</ButtonLink>
</CardFooter>
<CardFooter buttons="compact">...</CardFooter>
```

- Inherits `--card-pad` from the parent.
- Defaults to `padding-top: 0` (mirrors `CardHeader`'s `padding-bottom: 0`); the gap between Content and Footer comes from Content's bottom-pad. The parent restores the top-pad automatically for `variant="header-bar"`.
- `buttons="full"` (default): buttons and ButtonLinks stretch to full width and center their text. Use this when you want CTAs to span the card.
- `buttons="compact"`: buttons size to fit their content. Use for trailing-link style or when the button shouldn't dominate.
- `buttons="full"` (default): buttons, `ButtonLink`s, and `CardButtonFake`s stretch to full width and center their text. Use this when you want CTAs to span the card.
- `buttons="compact"`: buttons size to fit their content. Use for trailing-link style, multi-button footers, or when the button shouldn't dominate.
- `buttons="inherit"`: opt out of both — buttons render with their own intrinsic width.
- The layout variants target both real buttons (`[button]`) and `CardButtonFake` (`[data-label=button-link]`), so they apply whichever you use. Which one to use is the single-vs-multiple-CTA rule under [`Card` with `href`](#card-with-href): one CTA -> `CardButtonFake` in an `href` card; two+ -> real `ButtonLink`s in a non-link card.

### `CardButtonFake`

```tsx
<Card href="/proof-of-stake" hoverLift>
...
<CardFooter>
<CardButtonFake>Learn more</CardButtonFake>
</CardFooter>
</Card>
```

- The **only** correct way to render a footer CTA inside an `href` `Card`. It's a presentational `<div>` mirror of `Button` (rendered via `Button asChild`), so it reuses every Button style but is **not** an interactive element -- no nested `<a>`/`<button>` inside the card's anchor.
- Accepts Button's `variant`, `size`, and `isSecondary`. It carries `data-label="button-link"`, so `CardFooter`'s `buttons` layout variants size it exactly like a real button.
- Button's `hover-link` styling fires from the card's `group/link`, so hovering anywhere on the card animates the fake button identically to a real hover -- you don't (and can't) hand-wire hover styles onto it.
- Never use it on a non-link card (there's nothing to click) and never use a real `Button`/`ButtonLink` as the sole CTA of an `href` card (invalid nested anchor). See the single-vs-multiple-CTA rule above.

### `CardBanner`

Expand All @@ -158,8 +207,8 @@ When *should* you use `className`? Things that are genuinely outside the Card's
- `fit="contain"` with a *single* `<Image>` child triggers an auto-blurred-backdrop effect: the same image is cloned, scaled, blurred, and placed behind to fill any letterboxing. If you pass two children, you lose this magic and need to provide your own backdrop.
- `zoom`: `true` (default) propagates the parent `group/link` hover/focus into an image scale-up; pass `zoom={false}` when the art shouldn't move.
- Placement:
- Inside `CardHeader` (most common): the banner respects `--card-pad` and gets `--banner-radius`-rounded corners.
- As a direct child of `Card` (no `CardHeader` wrapper): the banner extends to the card's edges (no padding). Pair with `Card size="xs"` for an edge-to-edge image card, or use `variant="ghost"` for an outlined edge-to-edge look.
- **Inside `CardHeader` (the default -- prefer this):** the banner insets by `--card-pad` and its `--banner-radius` corners stay concentric with the card's outer radius. Required on **link** cards (`href`): the inset keeps the hover state clear of the image and the corner radii aligned.
- As a bare direct child of `Card` (no `CardHeader`): the banner extends flush to the card's edges. Only safe for a true edge-to-edge image, and pair it with `Card size="xs"` (`--card-pad: 0`) so the banner radius and the card's outer radius match. A bare banner on a padded size (`sm`/`md`/`base`) clips at `--banner-radius` while the card corner is `--card-pad + --banner-radius` -- a visible mismatch, and on a link card the hover treatment has no room to breathe. (See the bare-banner gotcha.)

### `CardEmoji`

Expand Down Expand Up @@ -253,10 +302,11 @@ import { Sparkles } from "lucide-react"

- [ ] Imports from `@/components/ui/card` (NOT `@/components/MarkdownCard` — that's the MDX shortcode wrapper)
- [ ] If linkable, uses `Card href="..."` (not a wrapping `<a>`)
- [ ] `hoverLift` on every card that carries an action (single CTA, multi-CTA, or text-link) -- skip it only on a pure navigation card with no interior button/link. Single CTA -> `href` + `CardButtonFake` + `hoverLift` (never a real `ButtonLink`/`Button` -- no nested anchor); two+ CTAs -> no `href`, real `ButtonLink`s + `hoverLift`; text-link-only -> no `href`, `InlineLink` + `hoverLift`
- [ ] No `className` on `Card`/parts that adjusts padding, background, spacing, border-radius, or text color — those go through variants
- [ ] Heading is `CardTitle`; uses `asChild` if `<h3>` would break heading outline
- [ ] Description is `CardParagraph`
- [ ] Image lives in a `CardBanner` (inside `CardHeader` for padded, or as a direct child of `Card` with `size="xs"` or `variant="ghost"` for edge-to-edge)
- [ ] Image lives in a `CardBanner`, wrapped in `CardHeader` (the default -- keeps banner radius concentric and gives link-hover room). Bare banner only for a true edge-to-edge image, paired with `size="xs"` so radii match
- [ ] If image needs containment, `fit="contain"` is used to get the auto-blur backdrop
- [ ] If you added a new variant case to `card.tsx`, story coverage is updated
- [ ] Tested in light AND dark mode
Expand Down
Loading
Loading