The problem
Transparent and pill headers are position: fixed — they sit on top of the page rather than pushing content down. With no compensation, the first row on the page renders at y=0 and the floating header visually overlaps it. Static headers (clean, dark, centered) don't have this issue because they take up space in the document flow.
A naive fix — adding top padding to every section — solves the crowding but breaks the vertical rhythm between mid-page rows. A naive fix in the other direction — adding padding only to <main> — leaves a strip of body background showing above heroes that were meant to bleed under the header.
The fix
WebProCMS adds the extra padding to the first eligible <section> only, and grows the section's existing top padding rather than replacing it. The hero's background still bleeds full-bleed to the top of the page (because the section grew, not shifted), and every other row keeps its normal spacing.
Only sections with py-section-hero or py-section qualify. A py-section row that lands after a hero is never affected — the targeting uses :first-of-type, so once a hero has claimed the bump, no later section gets it.
Per-header values
Each fixed-style header declares the bump it needs. Values are added on top of the section's existing top padding, so the final result respects the row's design.
| Header group | Extra top padding |
|---|---|
| Transparent (E1, E2) | 3rem |
| Pill (G1, G2) | 2rem |
| Everything else | 0 (no-op) |
Transparent headers occupy 6rem at rest with zero gap to a 6rem hero; the bump opens a real gap. Pill headers float at top-4 with h-13, leaving a smaller visible footprint, so the bump is correspondingly smaller.
How it works
RowGroupRegistry::headerPad() returns the value for the active header. The public layout reads it once per request and sets --header-pad as an inline style on <body>. A CSS rule in public.css adds it to the first eligible section via calc():
main > :first-child > section.py-section-hero:first-of-type {
padding-top: calc(var(--spacing-section-hero) + var(--header-pad, 0px));
}
When no fixed-style header is active, --header-pad is unset, the calc resolves to the section's normal token value, and there is no visual change.
What lives where
| Path | Purpose |
|---|---|
app/Support/Rows/RowGroupRegistry.php |
headerPad() returns the bump for a given header template name. |
resources/views/layouts/public.blade.php |
Looks up the active header's pad and sets --header-pad on <body>. |
resources/css/public.css |
The @layer utilities rules that target the first eligible <section> and add --header-pad to its padding-top. |
Notes
- The CSS rule covers both the live page structure (
main > div > section) and the page editor's preview iframe structure (main > div > [data-editor-row] > section), so the editor reflects the live result. - The bump never affects bottom padding — only
padding-topis grown. - No row-level opt-in / opt-out is required. Heroes that should bleed full-bleed under the header are the default; the extra padding lives inside the hero, so the hero still extends to
y=0.