Docs Block Developer Guide

Building a New Block — style.css

style.css loads on both the frontend and inside the editor iframe. Two governance rules enforced by the Block Validator apply to everything written in this file.


CSS Governance Rules

Rule CSS-01 — Token-only values. Every CSS value must reference a --wpt-* design token via var(). Hardcoded hex values, named colours, and raw pixel values for colour or spacing are rejected at upload. The only permitted exception is a fallback value inside a var() call.

/* ✅ Correct — token reference */
color: var(--wpt-color-text);

/* ✅ Correct — token with hardcoded fallback inside var() */
color: var(--wpt-color-text, #1a1a1d);

/* ❌ Rejected by validator — hardcoded hex */
color: #1a1a1d;

/* ❌ Rejected by validator — named colour */
color: black;

Rule CSS-02 — Scoped selectors. Every CSS selector must be scoped to the block’s own root class. Bare tag selectors (h2 { }, p { }, a { }) that would apply globally are rejected. This prevents styles written inside one block from bleeding into other blocks or the page layout.

/* ✅ Correct — scoped to block root */
.wpt-feature-card__heading { }
.wpt-feature-card h2 { }
.wpt-feature-card--bg-dark .wpt-feature-card__heading { }

/* ❌ Rejected by validator — global tag selector */
h2 { font-size: var(--wpt-text-xl); }

/* ❌ Rejected by validator — starts globally */
h2.wpt-feature-card__heading { }

Full style.css Template

/* ── Block root ────────────────────────────────────────────────────── */
.wpt-feature-card {
    padding:       var(--wpt-feature-card-padding, var(--wpt-spacing-xl));
    gap:           var(--wpt-feature-card-gap, var(--wpt-spacing-md));
    background:    var(--wpt-color-surface);
    border-radius: var(--wpt-radius-lg);
    box-shadow:    var(--wpt-elevation-card);
}

/* ── Heading ───────────────────────────────────────────────────────── */
.wpt-feature-card__heading {
    font-family: var(--wpt-font-heading);
    font-size:   var(--wpt-text-xl);
    color:       var(--wpt-color-text);
    margin:      0 0 var(--wpt-spacing-sm);
}

/* ── Description ───────────────────────────────────────────────────── */
.wpt-feature-card__description {
    font-family: var(--wpt-font-body);
    font-size:   var(--wpt-text-base);
    color:       var(--wpt-color-muted);
    line-height: var(--wpt-leading-relaxed);
}

/* ── Theme modifier ────────────────────────────────────────────────── */
.wpt-feature-card--theme-dark { color: var(--wpt-color-light); }

/* ── Background modifiers ──────────────────────────────────────────── */
.wpt-feature-card--bg-light     { background-color: var(--wpt-color-light);     }
.wpt-feature-card--bg-surface   { background-color: var(--wpt-color-surface);   }
.wpt-feature-card--bg-primary   { background-color: var(--wpt-color-primary);   }
.wpt-feature-card--bg-secondary { background-color: var(--wpt-color-secondary); }
.wpt-feature-card--bg-dark      { background-color: var(--wpt-color-dark); color: var(--wpt-color-light); }

/* ── Heading inverts on coloured backgrounds ───────────────────────── */
.wpt-feature-card--bg-primary   .wpt-feature-card__heading,
.wpt-feature-card--bg-secondary .wpt-feature-card__heading,
.wpt-feature-card--bg-dark      .wpt-feature-card__heading { color: var(--wpt-color-light); }

/* ── Responsive visibility ─────────────────────────────────────────── */
@media (max-width: 640px)                         { .wpt-feature-card--hide-mobile  { display: none !important; } }
@media (min-width: 641px) and (max-width: 1024px) { .wpt-feature-card--hide-tablet  { display: none !important; } }
@media (min-width: 1025px)                        { .wpt-feature-card--hide-desktop { display: none !important; } }

style.css rules:

  • No hardcoded hex colours — use var(--wpt-*) tokens. Hex values only inside var() as a fallback. (Validator enforced — CSS-01)
  • All selectors must be scoped to the block’s root class. No bare tag selectors. (Validator enforced — CSS-02)
  • No transition: or animation: declarations. The block validator rejects these (V2 Motion Layer reserved).
  • No @keyframes. Same reason.
  • No @import. Fonts are loaded globally by wpTruss.
  • Spacing is consumed from CSS custom properties set by render.php (e.g. var(--wpt-feature-card-padding)) — not hardcoded.
  • !important is only permitted on --hide-mobile, --hide-tablet, and --hide-desktop visibility helpers.