• Who we help
    • Sales
    • Marketing
    • Experts
  • Tools
    • Content Matrix
    • Voice Profile Builder
  • About
  • Writing
  • Book a call
Brand · Writing

Writing modules

The shared toolbox for building articles. Every block below is a reusable module: a fixed markup shape whose look and behavior come from shared CSS and JS. Copy a snippet, fill it with content, and it renders on-brand. Change a module in one place and every article updates.

The full spec lives in writing/MODULES.md. Brand rules come from the brand guidelines. New, interactive modules are tagged New.

Foundations
  • Eyebrow, thesis, highlight
  • Sections + containers
Hero + quotes
  • Article fold
  • Pull quote
  • Inline quote
  • Talking-head
Data viz
  • Stat band
  • Ranked bars
  • Ratio icons
  • Population grid
  • Chart: bar
  • Chart: line + toggle
  • Timeline
  • Framework matrix
Expressive
  • ASCII art
Lists + calls
  • Five-moves list
  • Offer card
  • Closing CTA
Foundation

Eyebrow, thesis, highlight

The structural label above every heading, the weightier lead paragraph, and the single-word Daffodil highlighter.

custom.cssno JS
Part 01 · Vendor distrust

Peer recommendations beat vendor marketing.

Senior buyers trust their peers far more than vendor marketing. That gap changes everything downstream.

Use the highlighter on one load-bearing word: it is the permission that earns the read. Two per page, light surfaces only.

<span class="eyebrow">Part 01 &middot; Vendor distrust</span>
<h2>Peer recommendations beat vendor marketing.</h2>
<p class="thesis">The one-sentence premise of the section.</p>
<p>It is the <span class="hl">permission</span> that earns the read.</p>
Foundation

Sections + containers

The vertical rhythm wrapper and reading-width containers. Backgrounds set the surface; blog posts stay on light surfaces.

custom.cssno JS

A .section.bg-ash with a .container-narrow inside.

Reading-width body copy sits in .container-narrow. Stat rows and charts use the wider .container-medium.

<section class="section bg-snow">
  <div class="container container-narrow">
    <!-- modules go here -->
  </div>
</section>
<!-- backgrounds: bg-snow | bg-ash | bg-ash-warm | bg-lagoon -->
<!-- widths: container-narrow | container-medium | container -->
Hero

Article fold

The standard opener: title and byline on the left, feature image on the right.

custom.cssno JS

Image slot shown as a placeholder.

Essay

Why your best content should reach the fewest people.

A case for niche over mass in B2B.

Evan Huston
Co-founder · June 12, 2026
Feature image
<section class="article-fold bg-snow">
  <div class="container">
    <div class="row align-items-center g-4 g-lg-5">
      <div class="col-lg-6 article-fold-text">
        <span class="eyebrow">Essay</span>
        <h1 class="article-fold-title">Piece title</h1>
        <p class="article-fold-sub">One-sentence summary.</p>
        <div class="article-byline">
          <img class="article-byline-avatar" src="..." alt="Author" />
          <div>
            <div class="article-byline-name">Author name</div>
            <div class="article-byline-meta">Title &middot; Month DD, YYYY</div>
          </div>
        </div>
      </div>
      <div class="col-lg-6 article-fold-media"><img src="..." alt="..." /></div>
    </div>
  </div>
</section>
Quote

Pull quote (Lagoon bookend)

The banked line on its own dark surface. One of the few places Lagoon is allowed in a blog post.

custom.cssno JS
The headline

“Adding community-driven data gives a report life. It opens up the voices of real people who buyers know and trust.”

<section class="section bg-lagoon">
  <div class="container container-narrow text-center">
    <span class="eyebrow">The headline</span>
    <p class="pull-quote" style="color:#fff;">A line worth its own surface.</p>
  </div>
</section>
Quote

Inline quote + attribution

An interview pull-quote within body copy, with a headshot, name, and role beneath it.

custom.cssno JS

“Tell me about my problem. Then tell me how you have seen people solve it, with or without your product.”

Lomit Patel
CMO, TYB
<p class="pull-quote">&ldquo;The quote.&rdquo;</p>
<div class="testimonial-attr d-flex align-items-center mt-3">
  <img src="..." alt="Name" loading="lazy" />
  <div>
    <div class="testimonial-attr-name">Name</div>
    <div class="testimonial-attr-role">Title, Company</div>
  </div>
</div>
Quote

Talking-head quote

A short clip of the person plays on hover or tap while the quote lights up word by word. Data-driven: the article holds the quote text and a word-timings JSON.

custom.csstalking-head.js

Needs a video clip, so it is not rendered here. See the live demo: /writing/talking-head-demo/.

<figure class="talking-head" data-th data-timings="/assets/video/<slug>/clip.timings.json">
  <div class="th-media">
    <video class="th-video" preload="metadata" playsinline muted poster="..." src="...mp4"></video>
    <span class="th-affordance" aria-hidden="true">Tap for sound</span>
  </div>
  <blockquote class="th-quote">
    <p class="th-text">The exact quote, as plain readable prose.</p>
    <figcaption class="testimonial-attr d-flex align-items-center th-attr">...name / role...</figcaption>
  </blockquote>
</figure>
<!-- add: <script src="/assets/js/talking-head.js" defer></script> -->
Data viz

Stat band

Big numbers that count up on scroll. Use a two- or three-column grid. Scroll it into view to see the count-up.

custom.cssreveal.js
47%

name industry peers as most trusted.

15%

name vendor content.

13%

name trade journalists.

<div class="row g-4 text-center">
  <div class="col-md-4 reveal-stat" data-reveal-delay="1">
    <span class="longform stat-feature">47<span class="stat-unit">%</span></span>
    <p class="text-muted-se">name <strong>industry peers</strong> as most trusted.</p>
  </div>
</div>
Data viz

Ranked bars

A 1/2/3 podium with a proportional fill behind each row, set with the --bar-width custom property.

custom.cssreveal.js
  1. 1Feeling understood
  2. 2Pricing & commercial terms
  3. 3Feature parity
<ol class="rank-list reveal">
  <li class="rank-1" style="--bar-width: 100%"><span class="rank-num">1</span><span class="rank-label">Feeling understood</span></li>
  <li class="rank-2" style="--bar-width: 46%"><span class="rank-num">2</span><span class="rank-label">Pricing</span></li>
  <li class="rank-3" style="--bar-width: 41%"><span class="rank-num">3</span><span class="rank-label">Feature parity</span></li>
</ol>
Data viz

Ratio icons

An "X to 1" people-icon comparison for a simple, memorable ratio.

custom.cssno JS
vs.

For every one buyer who starts with vendor marketing, three start by asking a peer.

<div class="ratio-icons" aria-hidden="true">
  <div class="ratio-group"><span class="ratio-person"></span><span class="ratio-person"></span><span class="ratio-person"></span></div>
  <span class="ratio-vs">vs.</span>
  <div class="ratio-group"><span class="ratio-person poppy"></span></div>
</div>
<p class="ratio-caption">The caption that reads the ratio.</p>
Data viz

Population grid

A waffle grid of dots for "N people, segmented." Below is a 50-dot illustration; the flagship report uses 400.

custom.cssno JS
30 · sub-$60k 13 · $60k-$100k 7 · $100k+
<div class="pop-viz" aria-hidden="true">
  <span class="dot"></span>          <!-- repeat for the bulk segment -->
  <span class="dot poppy"></span>    <!-- second segment -->
  <span class="dot daffodil"></span> <!-- third segment -->
</div>
<div class="pop-legend">
  <span><span class="swatch lagoon"></span>240 &middot; sub-$60k</span>
</div>
Data viz

Chart: bar New

A data-driven SVG bar chart. Animates in on scroll, shows a value readout on hover or focus, and emits a screen-reader data table automatically. The article carries only the data.

modules.csscharts.js
Most trusted source for upskilling
<figure class="se-chart" data-se-chart>
  <figcaption class="se-chart-title">Most trusted source for upskilling</figcaption>
  <script type="application/json" class="se-chart-data">
  {
    "type": "bar",
    "format": "percent",
    "categories": ["Industry peers", "Vendor content", "Trade journalists"],
    "series": [
      { "name": "All buyers", "color": "lagoon", "values": [47, 15, 13] }
    ]
  }
  </script>
</figure>
<!-- add: <script src="/assets/js/charts.js" defer></script> -->
Data viz

Chart: line + series toggle New

Multiple series render as legend chips. Click a chip to show or hide that series. Set "type": "area" for a filled version. Same data shape, same JS.

modules.csscharts.js
Peer content matters more as the deal grows
<figure class="se-chart" data-se-chart>
  <figcaption class="se-chart-title">Peer content matters more as the deal grows</figcaption>
  <script type="application/json" class="se-chart-data">
  {
    "type": "line",
    "format": "percent",
    "categories": ["$40-60k", "$60-100k", "$100k+"],
    "series": [
      { "name": "Peer content builds confidence", "color": "poppy", "values": [56, 60, 68] },
      { "name": "Feel the vendor understands them", "color": "lake", "values": [51, 58, 64] }
    ]
  }
  </script>
</figure>
Data viz

Timeline New

A vertical, editorial timeline. CSS only; add .reveal to each item for the fade-in.

modules.cssreveal.js
  1. Step 1

    Interview the buyer

    The act of being asked does pre-sale work no pitch can.

  2. Step 2

    Publish the peer content

    Today's interviewee becomes tomorrow's warm intro.

  3. Step 3

    Earn the right to teach

    Buyers read your thought leadership after the trust is built.

<ol class="se-timeline">
  <li class="se-timeline-item reveal">
    <span class="se-timeline-marker" aria-hidden="true"></span>
    <div class="se-timeline-content">
      <span class="se-timeline-date">Step 1</span>
      <h3 class="se-timeline-title">What happened</h3>
      <p>One or two lines of detail.</p>
    </div>
  </li>
</ol>
Data viz

Framework matrix New

A labeled 2-axis grid. Hover, tap, or keyboard-focus a cell to reveal its detail below. Data-driven, like the charts: the article carries only the config.

modules.cssmatrix.js
Every content type, mapped
<figure class="se-matrix" data-se-matrix>
  <figcaption class="se-matrix-title">Every content type, mapped</figcaption>
  <script type="application/json" class="se-matrix-data">
  {
    "xLabel": "Time", "yLabel": "Stance",
    "columns": ["Past", "Present", "Future"],
    "rows": ["Analytical", "Opinion"],
    "cells": [
      { "row": 0, "col": 0, "title": "Case study", "detail": "What happened, and why it worked." }
    ]
  }
  </script>
</figure>
<!-- add: <script src="/assets/js/matrix.js" defer></script> -->
Expressive

ASCII art New

Turn any image into detailed ASCII with scripts/asciify.mjs, then drop it in. The art scales to fit any screen. A tasteful wink, used sparingly.

modules.cssascii.jsasciify.mjs

Generated from a photo with asciify.mjs --cols 90 --figure.



                                    .,  ."!+-_[fj.
                               .^'~jc|XZdbWBM%BBacfl
                             ..:\_h&8@@M#&BB88BB%B@%Cl
                             "_{rdB@@%M##*##hoW&W#%@%J}^
                            .:lfkB%qr_!<]{1}-_--__{XoBZ_
                             '{u&*/<I;Ili>>>>><~++-[)U&w-
                             _[Jh]!l!!!i><<iii<~~_]}{)cMjI
                             {(Uz><<~+<illllIIllIi~-]{(pv-
                             }[CC(<~<~<illlI;;l!i<~~~-)kc[
                             ?[vaZ?<i>!I;;;;;::;;l>??])kY}
                             itLBJ[-?1{|rt-iI:i[uUYx//\O#x!
                            ')ta%r]txCLOYcXu?I[nzQJvJ1[ck)1.
                            ![-/o(+_{r(/{)fv_I]\(1[]]_+{c_}'
                            ^[+_Y}+ii!iii>[1!;__~ilII>~](-[
                             []_tj[~!;:;l+?-;;>-+il:;i+{[++
                             '?!?/\[_>![(\|\>I_)|1((<+[t-},
                              ^_}(t|\[|UYnnu\)|/fjXUj(\t[
                                 1|\tt[jnc}_<i!i?v\c1j)\,
                                 ;\j/jnx|1)[??-[)1txcn\>
                                ;[rjvrnrt\)}{{[]{tjtn['
                               _vzX\jvXzx\/(?~~[|ftt[
                              "juj1\(|/fnxuxjnzuttj(X-'
                             ^|XJXt;_[}}}tn\)[][1//([wJ<.
                           I)|xCqwUt,:~_+?\})/<}1(1f1;km/:
                        '>}fruXU0dbztI':ii~+~]?_+?[un,}%p}:
                     ;-)(rccJzUJCZohxr!".^;>i!li<+|YC,`k#mc<
                 ^<?{)fuccXYJZUYQmp*pm<;"..':i<+>-nCc?;|kd0}!'
              ,?}{|jnvzcvvYUUJLZ0Zb*MMY?:,.....'")v]^[+>wLLvt|1-~:
           ,-]])trfjruvuvzUUUYUYJ00ZkW8hnI.  .  .\?.'"l1OCUYczcx\}1I
        '++^':?\/fjxnccvuzJYUUYUJUJOqqpM%d)^    .1t:..IfOO0JYYYXXuj|[i
      .<~``';?/xxxxucvcvcXYYYYXzYJUUU0qpqoWaX]. .I,..:xQbp0JXXzJzccvu\}.
     '?:{[x/\tnvunvvvvcvvvXYYXYXYXUUUUJ0dpZqhqJi   .`/d8pJLJzzXYccYXXcr-
    `-^<rnuucXnrczzcvzzcvvczzXXUYzUYYYJCJ0qwQQQO+'.'}OkZJCUXzzcYzvcXXcnj'
   ']">ruunvXJzYXuuuvXzzucnvcXYXXvYYUXYJYUUU00UzXl "wJCULJJYzzczvnccccuv?
   ~;>|rnvncULXzXczcncXvuuuruvcXvzzcUzXYUYYYYJCUXj"{mzXJYzXzXucznncvvccXj'
  ll[-cXccvXUZLuYCcYJzcXzzvzcvXuccXYUXYzUzYYUCUUXcfJCzUCzcYYzczzvuxunccUvI
 '?\+nYzzccYJ0mXUQ0cCmvcvXcvvvccvvuccXYXzzYUJYYJYvzqQUJXzXccvcvcvxxunUcQz<
 -\|[cYUXzYYJLqLYQQLLaczUYXcvnczvcnczvzccXXYczzYzXnLkCYXYYXXzzuvvnuxnYzpc+
;}n(nXUCzvXCJCpZJmbm0#YzznvzXXccvncccccYvzzzccvXXzYLUzJXXzzczzccuxxrxcmmY>
''``^^^^^^^^^^","","":^^^^^``^^^```^^````^`^`^^^^^^^``^^^^^``^^^^^```^,^^.

ASCII portrait, generated from a photo
# Generate from any image (more columns = more detail):
node scripts/asciify.mjs photo.png --cols 110 --figure --label "Portrait" --out art.html

# Paste the markup it produces into your article:
<figure class="se-ascii" role="img" aria-label="Portrait">
  <pre class="se-ascii-art" aria-hidden="true">...art...</pre>
  <figcaption class="se-ascii-caption">Portrait</figcaption>
</figure>
<!-- add: <script src="/assets/js/ascii.js" defer></script> -->
# --invert gives light art for a Lagoon surface; --on-lagoon adds the dark panel.
List

Five-moves list

A list where each item leads with a stroked brand icon (2.5px stroke, square caps, miter joins).

custom.cssreveal.js
  • Lead with peers, not the product. The vendor takes the producer role, not the protagonist role.
  • Use peer content to earn the right to teach. Sequence matters.
<ul class="move-list">
  <li>
    <span class="move-icon" aria-hidden="true"><svg viewBox="0 0 24 24"><!-- paths --></svg></span>
    <span><strong>Lead with peers.</strong> The point of the move.</span>
  </li>
</ul>
Call

Offer card

The article CTA in a bordered card. Carries one Poppy button.

custom.cssno JS
Free for senior B2B revenue leaders

Get your free Market Map.

One 30-minute working session. Walk away with a complete view of your target market.

Book a call
<div class="offer-card">
  <span class="eyebrow">Free for senior B2B revenue leaders</span>
  <h2>Get your free Market Map.</h2>
  <p class="offer-pitch">One 30-minute working session...</p>
  <div class="offer-cta"><a href="/contact/" class="btn-se-primary">Book a call</a></div>
</div>
Call

Closing CTA

End every piece with one Poppy CTA. One per page.

custom.cssno JS
Get in touch

Talk to us.

One line that earns the click.

Book a call
<section class="section bg-snow text-center">
  <div class="container container-narrow">
    <span class="eyebrow">Get in touch</span>
    <h2 class="display-5 mb-3">Talk to us.</h2>
    <p class="thesis mb-4">One line that earns the click.</p>
    <a href="/contact/" class="btn-se-primary">Book a call</a>
  </div>
</section>
Splendid Engines
Nice day, isn't it?
  • Home
  • About
  • Writing
  • Contact
  • Brand
© 2026 Splendid Engines. Operating in Canada and the USA.