← alle entries

logo-lattice

FC-0148Testimonials & logo'slogo-latticeprimitivev1.0.0

Rooster van logotegels met een dragende lijn: twee kolommen (vier vanaf 48rem), elke tegel omlijnd, om en om een gedempt vlak, plusjes op de kruispunten; boven en onder een lijn over de volle vensterbreedte. Statisch.

zelfde component — andere tokens

Acht tegels in een rooster met dambordvlakken en plusjes op de kruispunten. Logo's van het project (URL of eigen SVG).

primitives/proof/logo-lattice.tsx
/**
 * LogoLattice — een rooster van logotegels met een dragende lijn: twee
 * kolommen (vier vanaf 48rem), elke tegel omlijnd, om en om een gedempt
 * vlak, en op de kruispunten kleine plusjes. Boven en onder loopt een lijn
 * over de volle breedte van het venster, buiten het rooster om.
 *
 * Geoogst uit 21st.dev Logo Cloud 2 (efferd): acht vaste CDN-logo's van
 * techmerken → `logos` van het project (src+alt of een node), lucide
 * PlusIcon → inline plus, de per-tegel Tailwind-klassen (border-r/-b,
 * md:bg-secondary, dark:brightness-0 dark:invert) → één patroon uit
 * nth-child en een `invert`-prop, w-screen-lijnen → 100vw met translate.
 */

import type * as React from "react";

export interface LatticeLogo {
  /** Beeld-URL óf een eigen node (SVG). */
  src?: string;
  node?: React.ReactNode;
  alt: string;
}

export interface LogoLatticeProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
  logos: readonly LatticeLogo[];
  /** Beelden omkeren naar de tekstkleur (voor donkere logo's op een donker vlak). */
  invert?: boolean;
  /** De lijnen boven en onder over de volle vensterbreedte. Default aan. */
  fullBleedLines?: boolean;
}

const Plus = ({ hoek }: { hoek: "rb" | "lb" }) => (
  <svg className={`fp-lattice__plus fp-lattice__plus--${hoek}`} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1} strokeLinecap="round" aria-hidden="true"><path d="M12 5v14M5 12h14" /></svg>
);

export function LogoLattice({ logos, invert = false, fullBleedLines = true, className, ...rest }: LogoLatticeProps) {
  return (
    <div {...rest} className={["fp-lattice", invert ? "fp-lattice--invert" : "", fullBleedLines ? "fp-lattice--lijnen" : "", className].filter(Boolean).join(" ")}>
      {logos.map((logo, i) => (
        <div key={`${logo.alt}-${i}`} className="fp-lattice__tegel">
          {logo.node ?? (
            // eslint-disable-next-line @next/next/no-img-element -- logo's van het project
            <img src={logo.src} alt={logo.alt} className="fp-lattice__beeld" loading="lazy" />
          )}
          {/* Plusjes op de kruispunten: rechtsonder op de eerste tegel van elke rij, plus linksonder in het midden. */}
          {i % 2 === 0 && i < logos.length - 2 && <Plus hoek="rb" />}
          {i % 4 === 2 && <Plus hoek="lb" />}
        </div>
      ))}
    </div>
  );
}
primitives/proof/proof.css
/**
 * Bewijs II — importeren in de app-root (registry-site: app/globals.css).
 * Token-contract 2.
 */

/* ── logo-lattice ───────────────────────────────────────────────────────── */
.fp-lattice { position: relative; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); width: 100%; border-left: var(--border-width) solid var(--border); border-right: var(--border-width) solid var(--border); color: var(--foreground); }
@media (min-width: 48rem) { .fp-lattice { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
/* De lijnen boven en onder, over de volle vensterbreedte, buiten het rooster. */
.fp-lattice--lijnen::before, .fp-lattice--lijnen::after { content: ""; position: absolute; left: 50%; width: 100vw; height: var(--border-width); background: var(--border); transform: translateX(-50%); pointer-events: none; }
.fp-lattice--lijnen::before { top: calc(-1 * var(--border-width)); }
.fp-lattice--lijnen::after { bottom: calc(-1 * var(--border-width)); }
.fp-lattice__tegel { position: relative; display: flex; align-items: center; justify-content: center; padding: var(--space-xl) var(--space-md); background: var(--background); border-bottom: var(--border-width) solid var(--border); }
@media (min-width: 48rem) { .fp-lattice__tegel { padding: var(--space-xl); } }
/* Twee kolommen: elke oneven tegel krijgt een rechterlijn; de laatste rij geen onderlijn. */
.fp-lattice__tegel:nth-child(odd) { border-right: var(--border-width) solid var(--border); }
.fp-lattice__tegel:nth-last-child(-n + 2) { border-bottom: 0; }
/* Dambord van gedempte vlakken: smal 1,4,5,8; breed 1,3,6,8 (om en om per rij). */
.fp-lattice__tegel:nth-child(4n + 1), .fp-lattice__tegel:nth-child(4n) { background: var(--muted); }
@media (min-width: 48rem) {
  .fp-lattice__tegel:nth-child(odd) { border-right: 0; }
  .fp-lattice__tegel:not(:nth-child(4n)) { border-right: var(--border-width) solid var(--border); }
  .fp-lattice__tegel:nth-last-child(-n + 4) { border-bottom: 0; }
  .fp-lattice__tegel:nth-child(4n + 1), .fp-lattice__tegel:nth-child(4n) { background: var(--background); }
  .fp-lattice__tegel:nth-child(8n + 1), .fp-lattice__tegel:nth-child(8n + 3), .fp-lattice__tegel:nth-child(8n + 6), .fp-lattice__tegel:nth-child(8n) { background: var(--muted); }
}
.fp-lattice__beeld { height: 1rem; width: auto; pointer-events: none; user-select: none; }
@media (min-width: 48rem) { .fp-lattice__beeld { height: 1.25rem; } }
.fp-lattice--invert .fp-lattice__beeld { filter: brightness(0) invert(1); }
.fp-lattice__tegel > svg:not(.fp-lattice__plus) { height: 1.25rem; width: auto; }
.fp-lattice__plus { position: absolute; z-index: 1; width: 1.5rem; height: 1.5rem; }
.fp-lattice__plus--rb { right: -0.75rem; bottom: -0.75rem; }
.fp-lattice__plus--lb { left: -0.75rem; bottom: -0.75rem; display: none; }
@media (min-width: 48rem) { .fp-lattice__plus--lb { display: block; } }