Trusted by outfitters and schools
Over two hundred shops, guide services and outdoor programmes run on Northwind gear.
- Harbor & Pine
- Tallgrass
- Lumen Co.
- Fieldnote
- Saltmarsh
- Bearing
- Kestrel
- Orchard Row
Logos in bordered cells beside a short statement, like a spec sheet.
ProofSectionNo dependencies Original, MIT
Over two hundred shops, guide services and outdoor programmes run on Northwind gear.
Using Claude Code? Give it the agent prompt and skip these steps.
foundation.css holds the tokens every component reads.
---
// src/layouts/Layout.astro
import "../styles/foundation.css";
--- ---
import LogoGrid from "../components/viewership/logo-grid/LogoGrid.astro";
---
<LogoGrid /> | Prop | Type | Default | Description |
|---|---|---|---|
title | string | · | Short headline beside the grid. |
body | string | · | Supporting line. |
items | { name: string; src?: string; href?: string }[] | · | Logos. Multiples of four fill the grid. |
as | "h2" | "h3" | "h2" | Heading element. |
---
/**
* LogoGrid: logos in bordered cells beside a short statement, like a spec
* sheet. Two columns when narrow, four when wide.
*
* @example
* <LogoGrid title="Trusted by outfitters" body="..." items={[{ name: "Kestrel" }]} />
*/
interface Item {
name: string;
src?: string;
href?: string;
}
interface Props {
title?: string;
body?: string;
items: Item[];
as?: "h2" | "h3";
class?: string;
}
const { title, body, items, as: Heading = "h2", class: className } = Astro.props;
---
<section class:list={["vs-logo-grid", className]}>
<div class="vs-logo-grid__inner">
{
(title || body) && (
<header class="vs-logo-grid__head">
{title && <Heading class="vs-display vs-logo-grid__title">{title}</Heading>}
{body && <p class="vs-logo-grid__body">{body}</p>}
</header>
)
}
<ul class="vs-logo-grid__cells">
{
items.map((it) => {
const mark = it.src ? <img src={it.src} alt={it.name} height="28" loading="lazy" /> : <span class="vs-display">{it.name}</span>;
return <li>{it.href ? <a href={it.href}>{mark}</a> : mark}</li>;
})
}
</ul>
</div>
</section>
<style>
.vs-logo-grid {
container-type: inline-size;
background: var(--vs-page);
color: var(--vs-ink);
font-family: var(--vs-font-sans);
font-stretch: var(--vs-font-stretch);
}
.vs-logo-grid__inner {
display: grid;
gap: 32px;
max-width: var(--vs-container);
margin-inline: auto;
padding: var(--vs-section-y) var(--vs-gutter);
}
.vs-logo-grid__title {
font-size: clamp(1.75rem, 4.5cqi, 2.75rem);
}
.vs-logo-grid__body {
max-width: 22rem;
margin: 1rem 0 0;
font-size: 0.9375rem;
line-height: 1.55;
color: var(--vs-ink-2);
}
.vs-logo-grid__cells {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
margin: 0;
padding: 0;
list-style: none;
border-top: var(--vs-border-w) solid var(--vs-line-strong);
border-left: var(--vs-border-w) solid var(--vs-line-strong);
}
.vs-logo-grid__cells li {
display: flex;
min-height: 96px;
align-items: center;
justify-content: center;
padding: 20px;
border-right: var(--vs-border-w) solid var(--vs-line-strong);
border-bottom: var(--vs-border-w) solid var(--vs-line-strong);
color: var(--vs-ink-2);
transition: background-color var(--vs-duration) var(--vs-ease-out);
}
.vs-logo-grid__cells li:hover {
background: var(--vs-surface);
color: var(--vs-ink);
}
.vs-logo-grid__cells span {
font-size: 1.25rem;
text-align: center;
}
.vs-logo-grid__cells img {
display: block;
width: auto;
height: 28px;
filter: grayscale(1);
opacity: 0.75;
}
.vs-logo-grid__cells a {
display: inline-flex;
color: inherit;
text-decoration: none;
}
.vs-logo-grid__cells a:focus-visible {
outline: none;
box-shadow: var(--vs-focus-ring);
}
@container (min-width: 720px) {
.vs-logo-grid__cells {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
}
@container (min-width: 960px) {
.vs-logo-grid__inner {
grid-template-columns: minmax(0, 1fr) minmax(0, 2.4fr);
gap: 56px;
align-items: start;
}
}
</style> Original, MIT.