diff --git a/docs/site/DESIGN-SPEC.md b/docs/site/DESIGN-SPEC.md index 8105695f6c4..cc14e51d828 100644 --- a/docs/site/DESIGN-SPEC.md +++ b/docs/site/DESIGN-SPEC.md @@ -149,120 +149,15 @@ Font: **Montserrat**, weight 800, letter-spacing 0.04em (applied via CSS) Completely swizzled (`src/theme/Footer/index.tsx`) — not using the Docusaurus built-in footer at all. -### Container -- Background: `#000000` -- Font: Nunito Sans, sans-serif -- Color: `#ffffff` - -### Logo + Tagline (left column) -- Logo image height: **32px** -- Brand text: **Montserrat** font, weight 800, letter-spacing 0.04em -- Tagline: *"Building the future on the efficient software frontier."* -- Company address: Erigon Technologies AG, Dammstrasse 16, 6300 Zug, Switzerland - -### Column Headers (all 4 columns) -- Font: **Montserrat**, weight 800 -- Size: 0.8rem -- Letter-spacing: 0.08em -- Text-transform: uppercase -- Color: `rgba(255,255,255,0.4)` - -### Column 1 — Products -| Label | URL | -|---|---| -| Erigon Client | `https://erigon.tech/products/erigon-client/` | -| Zilkworm | `https://erigon.tech/products/zilkworm/` | -| Cocoon | `https://erigon.tech/products/cocoon/` | -| R&D | `https://erigon.tech/products/rnd/` | - -### Column 2 — Developers -| Label | URL | -|---|---| -| Zilkworm Docs ↗ | `https://zilkworm.erigon.tech` | -| Cocoon Docs ↗ | `https://cocoon.erigon.tech` | -| Erigon Docs ↗ | `https://docs.erigon.tech` | -| Blog | `https://erigon.tech/blog/` | - -### Column 3 — Company -| Label | URL | -|---|---| -| About Us | `https://erigon.tech/about/` | -| Services | `https://erigon.tech/services/` | -| Contact | `https://erigon.tech/contact/` | -| Privacy Policy | `https://erigon.tech/privacy/` | -| Cookie Policy | `https://erigon.tech/cookies/` | - -### Column 4 — Community (icon + label) - -Each entry is an `` with `display:flex`, `alignItems:center`, `gap:0.6rem`. Icons are inline SVG React components, `width/height:18`, `fill:currentColor` so they inherit the link color on hover. - -| Platform | URL | -|---|---| -| X / Twitter | `https://x.com/erigoneth` | -| Discord | `https://dsc.gg/erigon` | -| GitHub | `https://github.com/erigontech` | -| LinkedIn | `https://www.linkedin.com/company/erigon/` | - -**SVG paths (all `viewBox="0 0 24 24"`, `fill="currentColor"`):** - -**X / Twitter** (`18×18`): -```svg - -``` +**This site uses the minimalistic docs footer**, not the full five-column one. Canonical description lives in `erigon-documents` → `public-docs/docusaurus-design-spec.md` §6.0–§6.3; do not duplicate it here. Summary only: -**Discord** (`18×18`): -```svg - -``` - -**GitHub** (`18×18`): -```svg - -``` - -**LinkedIn** (`18×18`): -```svg - -``` - -**Community column layout (React):** -```tsx -
- {[ - {label: 'X / Twitter', href: 'https://x.com/erigoneth', Icon: IconX}, - {label: 'Discord', href: 'https://dsc.gg/erigon', Icon: IconDiscord}, - {label: 'GitHub', href: 'https://github.com/erigontech', Icon: IconGitHub}, - {label: 'LinkedIn', href: 'https://www.linkedin.com/company/erigon/', Icon: IconLinkedIn}, - ].map(({label, href, Icon}) => ( - (e.currentTarget.style.color = '#EF7716')} - onMouseLeave={e => (e.currentTarget.style.color = 'rgba(255,255,255,0.65)')}> - - {label} - - ))} -
-``` - -### Footer Link Colors -| State | Color | -|---|---| -| Default | `rgba(255,255,255,0.65)` | -| Hover | `#EF7716` | -| Muted / address text | `rgba(255,255,255,0.35)` | - -### Bottom Bar -- Copyright: `© {year} Erigon Technologies AG. All rights reserved.` -- Inline links: Privacy Policy · Cookie Policy · Contact · hello@erigon.tech -- Font-size: 0.8rem -- Color: `rgba(255,255,255,0.35)` - ---- +- Single row: brand lockup → tagline → icon-only social buttons pushed right; then a divider and a bottom bar (copyright left, Privacy / Cookie / Contact / `hello@erigon.tech` right). +- No link columns and no postal address. Those belong to the **full** footer, which is erigon.tech's, not a docs site's. Do not "fix" this footer by porting them back. +- Nunito Sans throughout, with the `erigon.tech` wordmark in Quantify 700. +- Every interactive element hovers Erigon Orange `#EF7716`. +- Theme-aware surface via `--footer-*` custom properties in `src/css/custom.css`: white in light mode, black in dark. It is **not** always black — that was the previous five-column footer. +- Both modes carry a 1px top edge. In light because a white footer on a white body has no edge of its own; in dark because `#000000` against the `#0A0A0A` page body is imperceptible. +- Fine print is 0.55 alpha, deliberately not a mirrored 0.35: equal alpha is not equal perceived contrast across inverted surfaces, and 0.35 on white computes to 2.44:1, failing WCAG AA. ## 7. Typography @@ -280,7 +175,7 @@ All fonts are self-hosted via `@font-face` (no CDN). Woff2 files live in `static |---|---|---| | Body text | Nunito Sans, sans-serif | 400 | | Headings (h1–h6) | Montserrat, sans-serif | 800 | -| Navbar title, footer brand | Montserrat | 800 | +| Navbar title, footer brand (logo wordmark) | Quantify | 700 | | Navbar left items | Montserrat | 800 | | Code | system monospace | — | diff --git a/docs/site/docusaurus.config.ts b/docs/site/docusaurus.config.ts index 0841d9f465e..74449a9f083 100644 --- a/docs/site/docusaurus.config.ts +++ b/docs/site/docusaurus.config.ts @@ -186,16 +186,6 @@ export default async function createConfig(): Promise { }, ], }, - footer: { - style: 'dark', - links: [ - {title: 'Community', items: [ - {label: 'GitHub', href: 'https://github.com/erigontech/erigon'}, - {label: 'Discord', href: 'https://discord.gg/erigon'}, - ]}, - ], - copyright: `Copyright © ${new Date().getFullYear()} Erigon. Built with Docusaurus.`, - }, metadata: [ {name: 'description', content: 'Official documentation for Erigon — the efficient, modular Ethereum execution client built for performance and low disk footprint.'}, {name: 'theme-color', content: '#EF7716'}, diff --git a/docs/site/src/css/custom.css b/docs/site/src/css/custom.css index 994f772bf63..629fde45767 100644 --- a/docs/site/src/css/custom.css +++ b/docs/site/src/css/custom.css @@ -2,9 +2,16 @@ * Erigon brand colors — light and dark theme. * Primary: #EF7716 (Erigon Orange), Hover: #D4660F * Light bg: #FFFFFF, Dark bg: #0A0A0A - * Fonts: Montserrat ExtraBold (headings), Nunito Sans (body) + * Fonts: Montserrat ExtraBold (headings), Nunito Sans (body), + * Quantify 700 (brand wordmark — navbar title + footer brand, see + * docusaurus-design-spec.md §7 Font Roles) */ +/* Self-hosted fonts — no Google Fonts or CDN. See docusaurus-design-spec.md §7. + Quantify is non-free and licensed to Erigon: keep self-hosting this woff2, and + do not swap it for the CDN copy at fonts.cdnfonts.com/css/quantify. Licence + scope is recorded in the design spec, not here. */ + @font-face { font-family: 'Montserrat'; src: url('/fonts/Montserrat-ExtraBold.woff2') format('woff2'); @@ -37,6 +44,14 @@ font-display: swap; } +@font-face { + font-family: 'Quantify'; + src: url('/fonts/Quantify.woff2') format('woff2'); + font-weight: 700; + font-style: normal; + font-display: swap; +} + :root { /* Brand fonts */ --ifm-font-family-base: 'Nunito Sans', sans-serif; @@ -214,8 +229,8 @@ html { } .navbar__title { color: var(--ifm-navbar-link-color); - font-family: 'Montserrat', sans-serif; - font-weight: 800; + font-family: 'Quantify', sans-serif; + font-weight: 700; letter-spacing: 0.04em; } .navbar .clean-btn { @@ -444,40 +459,130 @@ details.alert.alert--info summary { color: var(--ifm-font-color-base) !important; } -/* Footer background matches dark brand */ -.footer { - background-color: #000000; +/* ─── Footer palette ─────────────────────────────────────────────────────── */ +/* Invariants: + - Surface follows the colour mode: white in light, black in dark. + - Text uses a fixed alpha ramp, not Infima's emphasis vars — those invert with + the theme and would render dark-on-black. + - Both modes carry a 1px top edge. A white footer on a white page body has no + edge of its own, and #000000 on the #0A0A0A page body is imperceptible. + - Fine print is 0.55, not 0.35: alpha is not perceptually symmetric across + inverted surfaces, and 0.35 on white is 2.44:1, below WCAG AA's 4.5:1. + - --footer-accent stays separate from --brand-accent, which is site-wide. */ +:root { + --footer-accent: #EF7716; + --footer-bg: #FFFFFF; + --footer-fg: #000000; + --footer-tagline: rgba(0, 0, 0, 0.55); + --footer-icon: rgba(0, 0, 0, 0.65); + --footer-fine: rgba(0, 0, 0, 0.55); + --footer-box-border: rgba(0, 0, 0, 0.25); + --footer-divider: rgba(0, 0, 0, 0.08); + --footer-top-edge: rgba(0, 0, 0, 0.08); +} +[data-theme='dark'] { + --footer-bg: #000000; + --footer-fg: #FFFFFF; + --footer-tagline: rgba(255, 255, 255, 0.55); + --footer-icon: rgba(255, 255, 255, 0.65); + --footer-fine: rgba(255, 255, 255, 0.55); + --footer-box-border: rgba(255, 255, 255, 0.15); + --footer-divider: rgba(255, 255, 255, 0.08); + --footer-top-edge: rgba(255, 255, 255, 0.08); +} + +/* ─── Footer interactive states ──────────────────────────────────────────── */ +/* These live in CSS, not in React onMouseEnter/onMouseLeave handlers. Those + handlers only fire for a mouse: keyboard users tabbing through the footer got + no accent state at all, and mutating inline styles fights any later theming. + :hover and :focus-visible are declared together so mouse, keyboard and touch + all get the same affordance. */ + +.footer-brand { + display: flex; + align-items: center; + gap: 0.6rem; + color: inherit; + text-decoration: none; + transition: color 0.15s; +} +.footer-brand:hover, +.footer-brand:focus-visible { + color: var(--footer-accent); + /* Infima's `a:hover` sets text-decoration: underline and outranks the base + class on specificity, so it has to be cleared on the state too. */ + text-decoration: none; } -/* ─── Footer social pill buttons ─────────────────────────────────────────── */ - -.footer-social-btn { +.footer-socials { display: flex; + flex-direction: row; + gap: 0.6rem; + flex-wrap: wrap; + margin-left: auto; +} + +.footer-social { + display: inline-flex; align-items: center; - gap: 0.65rem; - padding: 0.55rem 0.85rem; + justify-content: center; + width: 38px; + height: 38px; + border: 1px solid var(--footer-box-border); border-radius: 8px; - border: 1px solid rgba(255,255,255,0.15); - color: rgba(255,255,255,0.65) !important; - text-decoration: none !important; - font-size: 0.875rem; - font-weight: 500; - transition: color 0.2s ease, border-color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease; + color: var(--footer-icon); + text-decoration: none; + background: transparent; + transition: color 0.15s, border-color 0.15s, background-color 0.15s; +} +.footer-social:hover, +.footer-social:focus-visible { + color: var(--footer-accent); + border-color: var(--footer-accent); + /* Fallback first: color-mix() is Chrome/Edge 111+, Firefox 113+, Safari 16.2+, + and the default production browserslist still resolves down to Chrome 109. + Older engines keep the flat tint; newer ones take the color-mix line. Both + are the same colour, since --footer-accent is flat across modes here. */ + background-color: rgba(239, 119, 22, 0.12); + background-color: color-mix(in srgb, var(--footer-accent) 12%, transparent); +} + +.footer-legal { + font-size: 0.8rem; + color: var(--footer-fine); + text-decoration: none; + transition: color 0.15s; } -.footer-social-btn:hover { - color: #EF7716 !important; - border-color: #EF7716 !important; - transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(239,119,22,0.15); +.footer-legal:hover, +.footer-legal:focus-visible { + color: var(--footer-accent); + text-decoration: none; +} + +/* The icon buttons had no visible keyboard focus indicator at all. */ +.footer-brand:focus-visible, +.footer-social:focus-visible, +.footer-legal:focus-visible { + outline: 2px solid var(--footer-accent); + outline-offset: 2px; } /* ─── Footer responsive (mobile) ────────────────────────────────────────── */ @media (max-width: 768px) { + /* The footer top row is flex, not a grid — stack it, don't set columns. */ .footer-top-grid { - grid-template-columns: 1fr !important; - padding: 2.5rem 1.5rem 2rem !important; - gap: 2.5rem !important; + flex-direction: column !important; + align-items: flex-start !important; + padding: 1.5rem 1.5rem 1rem !important; + gap: 1rem !important; + } + + /* Socials are pushed right by margin-left:auto on desktop; drop that so they + align with the brand and tagline once stacked. Targets the dedicated class, + not > div:last-child, which broke if another div were appended. */ + .footer-socials { + margin-left: 0; } .footer-bottom-bar { diff --git a/docs/site/src/theme/Footer/index.tsx b/docs/site/src/theme/Footer/index.tsx index ccec85b53d6..58e66905e8b 100644 --- a/docs/site/src/theme/Footer/index.tsx +++ b/docs/site/src/theme/Footer/index.tsx @@ -25,167 +25,112 @@ const IconLinkedIn = () => ( ); +/* Brand stack per docusaurus-design-spec.md §7: Nunito Sans for body/UI text, + Quantify 700 for the wordmark only. Colours live in custom.css as --footer-* + vars so the surface can invert between light and dark mode. + Interactive states (hover, focus-visible, the accent tint) are CSS classes — + .footer-brand / .footer-social / .footer-legal — not handlers here. See + "Footer interactive states" in custom.css for why. */ +const BODY = 'var(--ifm-font-family-base)'; +const SURFACE = 'var(--footer-bg)'; +const FOREGROUND = 'var(--footer-fg)'; +const TAGLINE = 'var(--footer-tagline)'; +const FINE_BASE = 'var(--footer-fine)'; +const DIVIDER = 'var(--footer-divider)'; +const TOP_EDGE = 'var(--footer-top-edge)'; + +const SOCIALS = [ + {label: 'X / Twitter', href: 'https://x.com/erigoneth', Icon: IconX}, + {label: 'Discord', href: 'https://dsc.gg/erigon', Icon: IconDiscord}, + {label: 'GitHub', href: 'https://github.com/erigontech', Icon: IconGitHub}, + {label: 'LinkedIn', href: 'https://www.linkedin.com/company/erigon/', Icon: IconLinkedIn}, +]; + +const LEGAL = [ + {label: 'Privacy Policy', href: 'https://erigon.tech/privacy/'}, + {label: 'Cookie Policy', href: 'https://erigon.tech/cookies/'}, + {label: 'Contact', href: 'https://erigon.tech/contact/'}, + {label: 'hello@erigon.tech', href: 'mailto:hello@erigon.tech'}, +]; + export default function Footer(): React.ReactElement { const logoUrl = useBaseUrl('/img/logo-icon-orange.png'); return ( -