Files
Calvin b0db5b3a5c Perf pass + migrate home/payments page sections to shared (#3771)
* Perf pass + migrate home/payments page sections to shared

Performance work across every page plus a refactor moving home/payments
page sections into reusable shared components.

JavaScript
- Defer xrpl.js, jQuery, and Bootstrap in the global <head> (redocly.yaml)
  and on the 11 interactive tool pages' inline <script> tags — removes
  ~1MB of render-blocking JS from first paint on every page.
- Drop moment/moment-timezone from the client: remove the dead APEX-2025
  countdown in the navbar AlertBanner; format/parse dates with native
  Date/Intl in the blog and events pages.

Fonts & icons
- Remove render-blocking font-awesome.min.css + webfont. CSS glyphs
  (callouts, breadcrumbs, status labels, tutorials) now render as
  self-contained data-URI SVG masks (styles/_fa-icons.scss); markup icons
  use a shared inline-SVG <Icon> component (shared/components/Icon).
- Preconnect to Google Fonts / osano / GTM origins; preload the primary
  Booton UI font so text no longer waits on the 775KB stylesheet to parse.

Images & rendering
- Re-encode/resize oversized assets: new-york.jpeg 3.86MB→60KB,
  resources-icon.svg 4.2MB→150KB, event date/location SVGs ~1MB→0.5MB.
- Add loading=lazy/decoding=async to shared card components and the
  Video embed iframe.
- Add content-visibility to the events page's ~126-card grid to skip
  off-screen rendering.
- Earlier QA fixes: light-mode link color (#141414), events card grid
  layout, close nav menu on client-side navigation.

Home / payments refactor
- Move home page sections from page-local pages/home/sections/* to reusable
  shared/sections/* components (HomeHero relocated to shared/sections/HomeHero;
  home page now composes CalloutMediaBanner, CardStatsList, LinkTextDirectory,
  LogoSquareGrid, StandardCardGroupSection, FeatureSingleTopic, CarouselFeatured).
- Remove the now-unused pages/payments/sections/* in the same migration.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Revert FontAwesome removal (keep it for open-source flexibility)

Restore the FontAwesome webfont + font-awesome.min.css and all FA icon
usages (markup fa-* classes and CSS ::before glyphs), reverting the
consolidation in d4cf4e8581. Other performance changes from that commit
(deferred head scripts, moment removal, image optimization, lazy-loading,
content-visibility, preconnect/preload) are retained.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* cleaning up breadcrumb styling

* sticky side nav

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 14:03:02 -07:00

163 lines
4.3 KiB
TypeScript

import React, { useState } from 'react';
import clsx from 'clsx';
/** True when the logo URL is an SVG (file path or data URL) — used for dark neutral monochrome treatment. */
function isSvgLogoSource(src: string): boolean {
if (/^data:image\/svg\+xml/i.test(src)) return true;
const path = src.split(/[?#]/)[0].toLowerCase();
return path.endsWith('.svg');
}
export interface TileLogoProps {
/** Shape variant: 'square' (default) or 'rectangle' */
shape?: 'square' | 'rectangle';
/** Color variant: 'neutral' (default) or 'green' */
variant?: 'neutral' | 'green';
/** Logo image source (URL or path) */
logo: string;
/** Alt text for accessibility */
alt: string;
/** Click handler - renders as <button> */
onClick?: () => void;
/** Link destination - renders as <a> */
href?: string;
/** Disabled state - prevents interaction */
disabled?: boolean;
/** Additional CSS classes */
className?: string;
}
/**
* TileLogo Component
*
* A tile/card component designed to display brand logos with interactive states.
* Supports two shape variants (Square and Rectangle) and two color variants
* (Neutral and Green) with five interaction states (Default, Hover, Focused,
* Pressed, Disabled).
*
* Features a "window shade" hover animation where the hover color wipes from
* bottom to top on mouse enter, and top to bottom on mouse leave.
*
* Colors are defined in `TileLogo.scss` and follow `html.light` / `html.dark`.
* Green variant tokens: light mode uses green-200 (default), green-300 (hover /
* focus overlay), green-400 (pressed); dark mode uses green-300 (default),
* green-200 (hover / focus overlay), green-400 (pressed).
*
* Shape sizes are responsive and change based on breakpoints:
* - Square: 1:1 aspect ratio, width controlled by parent (use PageGridCol with span 2/2/3)
* - Rectangle: 9:5 aspect ratio, width controlled by parent (use PageGridCol with span 2/2/3)
*
* @example
* // Basic usage with link (square shape - default)
* <TileLogo
* variant="neutral"
* logo="/logos/partner-logo.svg"
* alt="Partner Name"
* href="/partners/partner-name"
* />
*
* @example
* // Rectangle shape with click handler
* <TileLogo
* shape="rectangle"
* variant="green"
* logo="/logos/featured-logo.svg"
* alt="Featured Partner"
* onClick={() => openPartnerModal('featured')}
* />
*
* @example
* // Disabled state
* <TileLogo
* variant="neutral"
* logo="/logos/coming-soon.svg"
* alt="Coming Soon"
* disabled
* />
*/
export const TileLogo: React.FC<TileLogoProps> = ({
shape = 'square',
variant = 'neutral',
logo,
alt,
onClick,
href,
disabled = false,
className = '',
}) => {
// Track hover state for animation
const [isHovered, setIsHovered] = useState(false);
// Build class names using BEM convention
const classNames = clsx(
'bds-tile-logo',
`bds-tile-logo--${shape}`,
`bds-tile-logo--${variant}`,
{
'bds-tile-logo--disabled': disabled,
'bds-tile-logo--hovered': isHovered && !disabled,
},
className
);
// Hover handlers
const handleMouseEnter = () => !disabled && setIsHovered(true);
const handleMouseLeave = () => setIsHovered(false);
const imageClassName = clsx(
'bds-tile-logo__image',
variant === 'neutral' &&
isSvgLogoSource(logo) &&
'bds-tile-logo__image--monochrome-white'
);
// Common content (overlay + logo image)
const content = (
<>
{/* Hover overlay for window shade animation */}
<div className="bds-tile-logo__overlay" aria-hidden="true" />
<img
src={logo}
alt={alt}
className={imageClassName}
aria-hidden="false"
loading="lazy"
decoding="async"
/>
</>
);
// Render as anchor tag when href is provided
if (href && !disabled) {
return (
<a
href={href}
className={classNames}
aria-label={alt}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
{content}
</a>
);
}
// Render as button (for onClick or disabled state)
return (
<button
type="button"
className={classNames}
onClick={onClick}
disabled={disabled}
aria-disabled={disabled}
aria-label={alt}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
{content}
</button>
);
};
export default TileLogo;