Skip to content

fix(card): decouple the horizontal card's button spacing from the section rhythm - #2150

Merged
markdumay merged 4 commits into
mainfrom
fix/card-horizontal-button-spacing
Sep 3, 2026
Merged

fix(card): decouple the horizontal card's button spacing from the section rhythm#2150
markdumay merged 4 commits into
mainfrom
fix/card-horizontal-button-spacing

Conversation

@markdumay

@markdumay markdumay commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Problem

A card with a button reserved the space beneath it with pb-{main.padding.y} on the row wrapper, then pulled the button down into that reserve with a hardcoded mb-n4:

<div class="card bg-light text-bg-lightpb-0">
  <div class="row-cols-2 row g-0 h-100 pb-5"><a class="… card-button card-button-link mb-n4">

main.padding.y is the rhythm between sections of a page. Using it here made the geometry inside a single card a function of a setting that has nothing to do with cards.

git log -L on the line shows how it got there: the class arrived as a literal pb-5 in 564a62e7 ("Add optional button to card"), hand-tuned against the button's negative margin. fe0d3553 ("Make padding globally configurable") then swept that literal up with the genuine section-level paddings.

Three things follow.

The gap tracks a setting that isn't about cards. It comes out as main.padding.y - 1.5rem, so raising the section rhythm inflates every card that carries a button. Below 4 the subtraction goes negative and the button hangs out of the card — so values the padding validator accepts break the layout.

No author can reach it. class lands on the .card element and the padding sits on the row wrapper one level down, so no utility class overrides it. The card's own padding argument — cascading, documented, and already the control the stacked branch uses for exactly this job — had no say.

class was destroyed anyway. The horizontal branch interpolated the colour style and the caller's class with no separator, so a card setting both color and class emitted one joined token: text-bg-lightpb-0. Both classes are lost. The stacked branch has always had the space; only this one is affected.

Found on a client site whose section rhythm is 5, where every horizontal card with a button carried a band of white space the editor could not remove — class="pb-0" landed on the wrong element and was mangled on the way there.

Change

Space the button with pt-{padding} and drop both the wrapper reserve and the negative margin. That makes the two branches agree, and puts the spacing under the argument that names it — padding=0 now means no gap, as it reads.

Also in scope, both found while tracing the above:

  • class separator on the horizontal .card element, plus a duplicated "class" key in the stacked branch's button call that Go's dict has always discarded.
  • wrapper passthrough in the card shortcode. It is declared in the card structure, so it reaches authors and the CMS snippet generator, but the shortcode initialised its own $wrapper to "" and only assigned to it for scrolling cards — the author's value never reached the partial. card-group passes its identically-named argument straight through, so the two have disagreed on an argument they share.

Tests

No page in the theme's own content renders a horizontal card with a button, which is why this drifted unnoticed. tests/templates now covers the path: seven cases over the .card classes, the row wrapper, the button's spacing column and the negative margin, plus a fixture page for the shortcode argument.

The harness sets main.padding.y = 5 deliberately — a value the card would otherwise have emitted — so a reintroduced coupling shows up as a pb-5 the assertions name, rather than as a silent absence. Padding runs at 0, 2, 3 and 5: the default alone would also be satisfied by a card that hardcoded the old middle value, while the ends are only reachable by one that actually reads the argument.

Reverting both templates fails six of the cases and exits non-zero. On the fix, hugo -s tests/templates is green (79 PASS, 0 FAIL), and CI runs it — test:templates is part of build-command, so it executed on all six platform/node combinations.

Compatibility

Patch. Cosmetic, and confined to where the button sits.

At the theme's default padding.y = 4, with the card's default padding = 3:

before after
above the button ~0 pt-3 → 1rem
below the button pb-4 + p-3mb-n4 → 1rem p-3 → 1rem

So a stock site keeps the space under the button unchanged and gains the card's own padding above it, where the negative margin previously left none. Sites that raised main.padding.y above 4 lose the extra band it was adding; a card that wants it back can ask with a larger padding. Sites below 4 stop having the button hang out of the card.

Sites that had worked around the mangled class by relying on the joined token will see the colour utility apply correctly again.

The horizontal branch interpolated the colour style and the caller's class
with no separator between them, so a card that set both `color` and `class`
emitted a single joined token: `bg-light text-bg-lightpb-0`. Both classes are
lost - the colour utility the theme generated and the class the author asked
for. The stacked branch a few lines down has always had the space; only this
one is affected, which is why it went unnoticed.

Also drop the duplicated "class" key in the stacked branch's button call. Go's
`dict` keeps the last value, so the first one has never had any effect.
@markdumay
markdumay changed the base branch from develop to main September 3, 2026 04:45
@markdumay markdumay closed this Sep 3, 2026
@markdumay markdumay reopened this Sep 3, 2026
…adding

A horizontal card with a button reserved its bottom space with
`pb-{main.padding.y}` on the row wrapper and then pulled the button down into
that reserve with a hardcoded `mb-n4`. `main.padding.y` is the site-wide
*section* rhythm, so the geometry inside a single card became a function of the
spacing between sections. It was not meant to be: the class arrived as a
literal `pb-5` alongside the button itself, and a later commit that made the
section padding configurable swept the literal up with the genuine
section-level ones.

Two things follow from the coupling. The gap under the button is whatever
`main.padding.y - 1.5rem` happens to be, so raising the section rhythm silently
inflates every horizontal card. And below 4 the subtraction goes negative and
the button hangs out of the card, so a value the padding validator accepts
breaks the layout.

Neither is reachable by the author, because `class` lands on the `.card`
element and the padding sits on the row wrapper one level down. The card's own
`padding` argument - already cascading, already documented, already the control
the stacked branch uses for exactly this job at the other end of the file - had
no say at all.

Space the button with `pt-{padding}` and drop both the wrapper reserve and the
negative margin, which makes the two branches agree and puts the spacing back
under the argument that names it. `padding=0` now means no gap, as it reads.

At the theme's own default padding of 4 the space under the button is
unchanged; what a stock site gains is the card's padding between the body text
and the button, where the negative margin used to leave none. Sites that raised
the section rhythm above 4 lose the extra band it was adding, and a card that
wants it back can ask for it with a larger `padding`.
`wrapper` is declared in the card structure, so it is offered to authors and
picked up by the CMS snippet generator, but the shortcode initialised its local
`$wrapper` to the empty string and only ever assigned to it when `scroll` was
set. Whatever the author wrote was dropped before the partial saw it. The
card-group shortcode passes its own `wrapper` straight through, so the two have
disagreed on an argument they share.

Read the argument, and let the scroll classes prepend to it rather than replace
it, so a scrolling card can carry an author class too.
Nothing in the build read the card's button markup, and no page in the theme's
own content renders a horizontal card with a button, so the drift that coupled
a card's internal spacing to the site's section rhythm had nowhere to show up.
These cases give that path a reader.

The harness sets `main.padding.y` to a value the card would otherwise have
emitted, so a reintroduced coupling is legible rather than merely absent: the
row wrapper is asserted in full, and the negative margin that paired with it is
pinned as its own column. The padding cases run at 0, 2, 3 and 5 - the default
alone would also be satisfied by a card that hardcoded the old middle value,
while the ends are only reachable by one that reads the argument.

Verified by reverting both templates to their previous state, which fails six
of the cases and exits non-zero.
@markdumay
markdumay force-pushed the fix/card-horizontal-button-spacing branch from 94874c1 to 9fd3eb4 Compare September 3, 2026 05:12
@netlify

netlify Bot commented Sep 3, 2026

Copy link
Copy Markdown

Deploy Preview for gethinode-demo ready!

Name Link
🔨 Latest commit 9fd3eb4
🔍 Latest deploy log https://app.netlify.com/projects/gethinode-demo/deploys/6a9901d8173d8d0008ddcd70
😎 Deploy Preview https://deploy-preview-2150--gethinode-demo.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@markdumay
markdumay merged commit 10e0c99 into main Sep 3, 2026
16 of 17 checks passed
@markdumay

Copy link
Copy Markdown
Collaborator Author

🎉 This PR is included in version 3.23.7 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@markdumay
markdumay deleted the fix/card-horizontal-button-spacing branch September 3, 2026 06:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant