ph/components

Logo grid

Sponsor logos sized to equal weight, in color on hover.

Code

Copy into src/components/ph/ .

logo-grid.css
/* --logo-base sets the em unit the normalized logo sizes are written in. */
.ph-logo-grid {
  --logo-base: 32px;
  font-size: var(--logo-base);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.9em 1.2em;
}

@media (min-width: 768px) {
  .ph-logo-grid {
    --logo-base: 45px;
    gap: 1em 1.75em;
  }
}

.ph-logo-grid__cell {
  position: relative;
  display: block;
  flex: none;
}

.ph-logo-grid__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* Static silhouette. Tweening invert() toward none passes through flat gray,
   so the brand copy crossfades in on top instead. */
.ph-logo-grid__img--wash {
  filter: brightness(0);
}

.dark .ph-logo-grid__img--wash,
[data-tone="dark"] .ph-logo-grid__img--wash {
  filter: brightness(0) invert(1);
}

.ph-logo-grid__img--brand {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 0.2s ease;
}

.ph-logo-grid__cell:is(:hover, :focus-visible) .ph-logo-grid__img--brand {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .ph-logo-grid__img--brand {
    transition: none;
  }
}
LogoGrid.astro
---
import "./logo-grid.css";

type Logo = {
  name: string;
  src: string;
  href?: string;
  /** Size normalized around a 48px base. Omit for a plain 0.75em-tall logo. */
  width?: number;
  height?: number;
  /** Optical-centering nudge in em, e.g. "translate(0em, -0.02em)". */
  transform?: string;
};

type Props = {
  logos: Logo[];
  tone?: "light" | "dark";
  class?: string;
};

const BASE = 48;
const { logos, tone, class: className } = Astro.props;
---

<div data-tone={tone} class:list={["ph-logo-grid", className]}>
  {
    logos.map((l) => {
      const Cell = l.href ? "a" : "div";
      const size = l.width && l.height ? `width: ${l.width / BASE}em; height: ${l.height / BASE}em;` : "height: 0.75em; width: auto;";
      return (
        <Cell
          class="ph-logo-grid__cell"
          href={l.href}
          target={l.href ? "_blank" : undefined}
          rel={l.href ? "noopener noreferrer" : undefined}
          style={`${size}${l.transform ? ` transform: ${l.transform};` : ""}`}
        >
          <img class="ph-logo-grid__img ph-logo-grid__img--wash" src={l.src} alt={l.name} width={l.width} height={l.height} loading="lazy" decoding="async" />
          <img class="ph-logo-grid__img ph-logo-grid__img--brand" src={l.src} alt="" aria-hidden="true" width={l.width} height={l.height} loading="lazy" decoding="async" />
        </Cell>
      );
    })
  }
</div>