fix: #3620731 Correct the brand typography bindings and add the mobile type scale

Issue: https://www.drupal.org/i/3620731

Corrects the four brand typography bindings, adds the mobile type scale, and moves the CKEditor editor stylesheet with them so the editor and the front end cannot drift apart.

The variable corrections

before after
brand serif on $font-family-monospace $font-family-serif, applied to prose only
$font-family-monospace 'EB Garamond', serif a real monospace stack
$headings-font-weight 700 500
$headings-line-height 140% 1.35
body colour Bootstrap default #212529 #242424

The serif being parked on the monospace variable had a measurable consequence: code, pre and kbd were the serif's only consumers and they render at weight 400, so the EB Garamond 700 face the theme loads rendered nowhere on the site. Both faces were already vendored and attached with the right weights, so this was purely a wiring bug.

Three new type tokens, which are one coherent change because the 18px base makes Bootstrap's em-relative and multiplier-derived sizes land off the design:

  • $font-size-md: 1rem for the design's Body/Regular/Small step. There was no 1rem token: the base is 18px and $font-size-sm is 14px.
  • $small-font-size: $font-size-md. Bootstrap's default is 0.875em, which resolves against the 18px parent and lands at 15.75px against the design's 16px. Root-relative on purpose: em here would reintroduce the drift.
  • $lead-font-size: 1.25rem. Bootstrap derives this as $font-size-base * 1.25, which the 18px base turns into 22.5px against the design's 20px.

$headings-font-family is now pinned to the sans family explicitly rather than left null, because a prose wrapper sets the serif on its subtree and a null value would let headings inherit it.

Prose serif, sans chrome

The design uses the serif for prose and the sans for interface: its header renders nav items and the Newsletter button in Rubik, and the Foundation tables use Rubik for header rows with the serif only for prose specimens. So $font-family-base stays Rubik and the serif is applied to prose scopes only. Setting $font-family-base to the serif would have put EB Garamond on every button, form control, nav item and admin string.

--ck-content-font-family is set for the editor. CKEditor ships its own .ck-content { font-family: var(--ck-content-font-family) } in an inline stylesheet, which is the same specificity as a theme rule and comes later in the document, so a plain declaration loses and the editor fell back to Helvetica. Feeding CKEditor's own custom property works with that mechanism instead of escalating specificity or reaching for !important. inspect() preserves the quotes; plain interpolation stripped them.

The rich-text class hook is correct but unexercised. It gives text-format prose a hook so the theme layer can style it while the component keeps its zero-CSS contract. On a site whose content is authored through rich-text it works; varbase_demo_content drops raw markup straight into a Canvas section column slot and bypasses the component entirely, so the shipped demo content renders sans. Verified on a real page: rich-text appears in none of the component ids and is referenced by no content. Stated plainly rather than papered over with a broader selector, because the obvious alternative (.sdc-section__inner) catches chrome as well as prose.

Mobile type scale, with two cells deliberately left out

Added: H1 38, H4 18, H5 16, H6 12, body 16, lead 18, small 14. Desktop heading sizes already matched the design exactly and are untouched.

Mobile H2 and H3 are absent on purpose, and stay at their desktop values with a comment in the SCSS explaining why. The two cells are absent for different reasons, and the earlier framing of both as "anomalous sizes" was wrong:

  • Mobile H2 = 14 is not an error. The design's 14px uppercase style is the section eyebrow, which is what the section label binds its text to. So the specimen table is mixing a label style into a heading scale. The question for the designer is whether that cell is bound to the wrong style, not whether 14px is the intended heading size.
  • Mobile H3 = 30, larger than desktop's 24, remains genuinely unexplained and is still open. Its specimen binds Mobile/Heading/H3/Large-Default while desktop uses Medium-Default.

Worth recording for any future token mapping: desktop H2's 42px exists only as a local override in the specimen and is bound to no style at all. $h2-font-size: 2.625rem is the right value, and it can only be taken from the specimen table, but nothing can bind to Desktop/Heading/H2 and reaching for it would ship 14px uppercase headings across the theme.

Leaving both cells alone is not a gap: Bootstrap's RFS is enabled, so they fall back to fluid sizes that measure 28.39px and 21.53px at 375px on a real page, a coherent ramp under H1 38. That measurement is the evidence for the designer question.

Riding along, and why

views-view-unformatted group title. Included because it is heading structure, which is the same subject as the heading scale, and because it fixes a defect rather than adding a feature. The existing include passed html_tag and content to atoms/heading, and that component declares neither - its API is heading_text and level. So the views group title rendered as an empty <h3> and the text was dropped. Rendered to confirm rather than inferred:

before: <h3 data-component-id="…:heading"> </h3>          title text: DROPPED
after:  <h3 …>Latest articles</h3>                        title text: PRESENT
after:  <h2 …>Latest articles</h2>  (title_level: 2)      title text: PRESENT

title_level is an integer prop, enum [2..6], default 3, so every existing listing renders exactly as before. This forces no recipe re-export. The component carries noUi: true and use_in_views: true, and Canvas's own SingleDirectoryComponentDiscovery ignores noUi SDCs at discovery, throws ComponentDoesNotMeetRequirementsException(['Component flagged "noUi".']) in checkRequirements(), and treats them as disabled. So status: false with active_version: fallback is the correct expected state for it, not staleness.

Support work, folded in rather than left homeless: a manifest lint gate (yarn lint:components) that fails on the empty-examples and empty-enum mistakes Canvas refuses a component over and only warns on the authoring conventions, with a pinned baseline for pre-existing debt; .cspell.json words; and a .gitignore line for the bytecode the script would otherwise leave untracked.

Where the measurements come from

Two environments, and the distinction matters:

  • A real Drupal site, drupal/cms with drupal/rightup 1.0.x-dev installed via drush site:install drupal_cms_installer installer_site_template_form.add_ons=rightup, default theme vartheme_bs5_rightup, carrying this branch's theme work overlaid on the installed theme. Everything below was measured there, anonymously, as computed styles.
  • An earlier static harness serving the compiled CSS was rejected as a mockup, and rightly: it "proved" two things the real site then disproved, namely the prose selectors (which match nothing in real output) and the CKEditor serif (which was losing to the inline rule). Both are corrected above.

Measured at 1440px: h1 52/500/70.2px, h2 42/500/56.7px, h3 24/500/32.4px, h4 20/500/27px, h5 16/500/21.6px, h6 14/500/18.9px, all Rubik at rgb(36,36,36); .lead 20px; small 16px; prose EB Garamond 18px with strong at EB Garamond 700 and document.fonts.check('700 1rem "EB Garamond"') true; a heading inside prose staying Rubik; code on the monospace stack; .ck-content prose matching front-end prose in a live editor instance, with a canvas width probe confirming the face genuinely renders rather than falling back.

Measured at 375px: h1 38, h4 18, h5 16, h6 12, body 16, lead 18, small 14, no horizontal overflow.

Build and lint: yarn install, yarn theme:build and yarn components:build all exit 0 with zero Sass errors; yarn lint:components reports 0 errors; php -l clean; stylelint clean on the generated CSS; cspell clean on the changed files. phpcs is not installed in the verification project, so it was not run - only the checks named here.

Two notes for the reviewer

A constraint worth recording for later. $font-family-serif and $font-size-md are introduced by this MR and do not exist on 1.0.x at 331128e4. Component work that consumes them (the media-banner caption is the case in hand) therefore cannot compile without this change: yarn components:build fails on an undefined variable. That component work is not currently proposed for merge, so there is nothing to sequence against today. This note exists so that whenever it is proposed, the dependency is already known rather than rediscovered as a build failure.

Branch history. This branch carries three commits: the change itself, a correction described below, and a documentation-only commit that rewrites the comment explaining why mobile H2 and H3 are excluded (no value changed, and the compiled CSS is byte-identical because SCSS line comments are stripped). The correction: the first commit was built from a working tree that predated the merge of #3620716 and therefore reverted its branding wordmark rules; the second restores them. drupal.org's server hooks refuse branch deletion, so the mistake is corrected in place rather than rewritten. Squash is enabled, and the branch tip is verified to carry both the merged branding rules and this change: scss/base/vartheme.base.scss holds the #3620716 block byte-identical to canonical plus the new prose and mobile sections, and the compiled CSS carries both.

Two build artifacts in the diff are consequences of the body-colour change rather than hand edits, and are easy to misread as unrelated: components/base/root/root.css is where :root actually lives, because bootstrap/scss/root is deliberately commented out of bootstrap.base.scss in favour of the base/root component, so token changes only regenerate under yarn components:build; and components/molecules/accordion-block/accordion-block.css changes one line because the accordion chevron is a data-URI SVG whose stroke derives from $body-color.

AI-Generated: Yes

Checkpoints:

  • File an issue
  • Addition/Change/Update/Fix
  • Testing to ensure no regression
  • Automated unit testing coverage
  • Automated functional testing coverage
  • UX/UI designer responsibilities
  • Readability
  • Accessibility
  • Performance
  • Security
  • Developer Documentation
  • User Guide Documentation
  • Reviewed by human
  • Code review by maintainers
  • Full testing and approval
  • Credit contributors
  • Review with the product owner
  • Release notes snippet
  • Release
Edited by Rajab Natshah

Merge request reports

Loading
Loading