StatsSplit

apps/www/src/sections/StatsSplit.astro · 77 lines · group Stats

StatsSplit — kicker, title, text and the numbers (<Stat>) on the left, the picture as a rounded card on the right. Use for: a company-in-numbers block on a light page, "about us" with figures, a product with a photo and its key parameters. Not for: the dark full-width band (<StatsBanner>); a bare row of figures (<StatsRow>). Children: <Stat> tags, two to four (they fill two columns). Picture: `image` (file name in images/) is cropped to fill the card — a photo, not a logo. Buttons: a button appears only when its text is given.

In the fleet

Not used by any site yet.

Samples 1

sample

THE SEASON SO FAR

What the network noticed this spring

Frost nights arrive earlier and end later than the regional forecast suggests — and they differ between two fields a kilometre apart.

The figures below are the ones growers quote back to us most often.

frost nightsacross the network, March to May
41
between neighbourstypical difference between two nearby fields
2,3 °C
MDX of this sample
<StatsSplit
  kicker="THE SEASON SO FAR"
  title="What the network noticed this spring"
  description={[
    'Frost nights arrive earlier and end later than the regional forecast suggests — and they differ between two fields a kilometre apart.',
    'The figures below are the ones growers quote back to us most often.',
  ]}
  image="photo-02.jpg"
  primaryText="Read the report"
  primaryHref="/report/"
>
  <Stat value="41" label="frost nights" detail="across the network, March to May" />
  <Stat value="2,3 °C" label="between neighbours" detail="typical difference between two nearby fields" />
</StatsSplit>

Props

NameTypeRequiredDefaultDescription
idstringanchor of the section
kickerstringshort uppercase line above the title
titlestringthe section heading
descriptionstring | string[]one paragraph, or several as an array
theme'light' | 'muted' | 'dark' | 'brand' | 'brand-dark'background of the section
imagestringfile name in images/, cropped to fill the card on the right
primaryTextstringtext of the solid brand button; omit to hide the button
primaryHrefstringlink of the primary button
secondaryTextstringtext of the second, outlined button
secondaryHrefstringlink of the secondary button

Source apps/www/src/sections/StatsSplit.astro @ gitt.one

---
/**
 * StatsSplit — kicker, title, text and the numbers (<Stat>) on the left, the
 *   picture as a rounded card on the right.
 *
 * Use for: a company-in-numbers block on a light page, "about us" with
 *   figures, a product with a photo and its key parameters.
 * Not for: the dark full-width band (<StatsBanner>); a bare row of figures
 *   (<StatsRow>).
 * Children: <Stat> tags, two to four (they fill two columns).
 * Picture: `image` (file name in images/) is cropped to fill the card — a
 *   photo, not a logo.
 * Buttons: a button appears only when its text is given.
 */
import { Image } from 'astro:assets';
import { resolveImage } from '../lib/images';
import Section from './_Section.astro';
import type { Theme } from './_Section.astro';
import Button from './_Button.astro';

interface Props {
  /** anchor of the section */
  id?: string;
  /** short uppercase line above the title */
  kicker?: string;
  /** the section heading */
  title?: string;
  /** one paragraph, or several as an array */
  description?: string | string[];
  /** background of the section */
  theme?: Theme;
  /** file name in images/, cropped to fill the card on the right */
  image?: string;
  /** text of the solid brand button; omit to hide the button */
  primaryText?: string;
  /** link of the primary button */
  primaryHref?: string;
  /** text of the second, outlined button */
  secondaryText?: string;
  /** link of the secondary button */
  secondaryHref?: string;
}
const p = Astro.props;
const theme = p.theme ?? 'light';
const paragraphs = p.description === undefined ? [] : Array.isArray(p.description) ? p.description : [p.description];
const img = p.image ? await resolveImage(p.image) : undefined;
const primaryHref = p.primaryHref ?? '#';
const secondaryHref = p.secondaryHref ?? '#';
const dark = theme === 'dark' || theme === 'brand';
---

<Section id={p.id} theme={theme}>
  <div class="lg:grid lg:grid-cols-2 lg:items-center lg:gap-16">
    <div>
      {p.kicker && (
        <p class="text-sm font-semibold uppercase tracking-wide">
          <span class="bg-gradient-to-r from-primary-500 to-primary-alt-600 bg-clip-text text-transparent [.bg-stone-800_&]:from-primary-300 [.bg-stone-800_&]:to-primary-alt-400">{p.kicker}</span>
        </p>
      )}
      {p.title && <h2 class="mt-3 text-3xl font-extrabold tracking-tight sm:text-4xl">{p.title}</h2>}
      <div class:list={['mt-6 space-y-4 text-lg', dark ? 'text-gray-300' : 'text-gray-500']}>
        {paragraphs.map((x) => <p>{x}</p>)}
      </div>
      <dl class="mt-10 grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-2" data-variant="split">
        <slot />
      </dl>
      {(p.primaryText || p.secondaryText) && (
        <div class="mt-10 flex flex-wrap gap-4">
          {p.primaryText && <Button href={primaryHref}>{p.primaryText}</Button>}
          {p.secondaryText && <Button href={secondaryHref} kind="secondary" onDark={dark}>{p.secondaryText}</Button>}
        </div>
      )}
    </div>
    {img && <Image src={img} alt="" widths={[400, 840]} class="mt-12 w-full rounded-2xl object-cover shadow-xl lg:mt-0" />}
  </div>
</Section>

Source links point to engine-astro@7b686388. Samples are demo content: the components are the real ones, the copy and the pictures are made up.