Skip to content

[#363] Added a 'card' banner type that shows the featured image uncropped. - #364

Merged
AlexSkrypnyk merged 2 commits into
developfrom
feature/363-card-banner
Nov 7, 2026
Merged

AlexSkrypnyk merged 2 commits into
developfrom
feature/363-card-banner

Conversation

@AlexSkrypnyk

@AlexSkrypnyk AlexSkrypnyk commented Nov 7, 2026 •

Copy link
Copy Markdown
Member

Closes #363

Before / After

BEFORE (every type with a featured image)
┌──────────────────────── .ct-banner__inner ───────────────────────┬──────────────────────┐
│ .container                                                       │ __featured-image__   │
│  title, intro  (col-m-6)                                         │ wrapper, absolute,   │
│                                                                  │ 40% wide, full height│
│                                                                  │ banner_featured      │
│                                                                  │ 4:3 focal crop       │
│                                                                  │ + object-fit: cover  │
│                                                                  │ display:none < 768px │
└──────────────────────────────────────────────────────────────────┴──────────────────────┘

AFTER (type = card)
┌──────────────────────────── .ct-banner__inner ───────────────────────────────┐
│ .container > .row.ct-banner__card-layout  (align-items: center)              │
│ ┌──────── col-m-7 ────────────────┐  ┌──────── col-m-5 ────────────────┐     │
│ │ breadcrumb, title, intro        │  │ .ct-banner__card                │     │
│ │                                 │  │  img via banner_card            │     │
│ │                                 │  │  scale to fit 960x960, WebP     │     │
│ │                                 │  │  own ratio, radius + shadow     │     │
│ └─────────────────────────────────┘  └─────────────────────────────────┘     │
│ < 768px: the card stacks below the intro, full container width               │
└──────────────────────────────────────────────────────────────────────────────┘
default / large / intro: markup and banner_featured unchanged

Checklist before requesting a review

  • Subject includes ticket number as [#123] Verb in past tense.
  • Ticket number #123 added to description
  • Added context in Changed section
  • Self-reviewed code and commented in commented complex areas.
  • Added tests for fix/feature.
  • Relevant tests run and passed locally.

Changed

Editors can now pick card as the banner type on project, blog and civictheme_page nodes and on the civictheme_banner block. A card banner renders the featured image inside .container as the second grid column, through the new banner_card image style, at the image's own aspect ratio on desktop and on phones.

The existing banner pushes the featured image through banner_featured (a 1090x818 focal-point crop) and then object-fit: cover into a box 40% wide and as tall as the banner, and hides that box below 768px. When the image is a designed graphic rather than a photo, such as the Behat Screenshot and Git Artifact work pages, the logo and the code are cut off on desktop and nothing shows on mobile.

After merge, nothing changes on screen until an editor switches a page to card. Pages on default, large and intro keep their markup and banner_featured. The 1 behaviour fix outside card: when a node says inherit, the theme now passes the banner block's type instead of the literal inherit (which Twig rendered as default). The only banner block is default, so no page renders differently today.

  1. Config
    • New image.style.banner_card: image_scale 960x960 with upscale off, then image_convert to WebP. No crop effect.
    • card|Card added to the allowed values of field.storage.node.field_c_n_banner_type and field.storage.block_content.field_c_b_banner_type. Adding a value is allowed on a field with data, so drush cim applies it without a deploy hook.
  2. Preprocess (includes/banner.inc)
    • _drevops_preprocess_block__civictheme_banner() resolves the effective type once: the node's value, or the block's when the node says inherit.
    • _drevops_banner_apply_image_styles() takes that type and resolves the featured image through banner_card for card, banner_featured otherwise. Both style names stay literal arguments so ImageStyleConfigTest finds them when it scans the theme.
  3. Component (components/03-organisms/banner/)
    • banner.twig whitelists card. For card it wraps the text rows in col-xxs-12 col-m-7 and renders the image in a sibling col-xxs-12 col-m-5, and skips the absolute __featured-image__wrapper. The 4 repeated featured_image ? col-m-6 ternaries became 1 column_class variable.
    • banner.scss adds .ct-banner-type--card: the row is vertically centred from the m breakpoint, the card gets a top margin when stacked, and the image is display: block; width: 100%; height: auto with $ct-banner-card-border-radius and $ct-banner-card-shadow from variables.components.scss.
    • banner.component.yml and the Storybook type control list card.
    • The image atom already fills width and height through do_image_dimensions(), which applies the style's transform, so the card reserves its space before loading (for example 960x549 for a 1344x768 source).
  4. Tests
    • ImageStyleConfigTest::testCardImageScalesWithoutCrop() asserts the 960x960 bounds, no upscale and exactly image_scale then image_convert. The existing exported and WebP checks pick up banner_card from the theme scan (15 tests).
    • New banner_card.feature: card is offered on the node and block forms; a card project renders the image inside the content grid through banner_card as WebP with no side wrapper; a default project still renders the side wrapper through banner_featured.
  5. Docs: docs/performance.md lists banner_card and describes when the card type uses it.

After deploy, switch the Behat Screenshot and Git Artifact work pages to card in the editor. This PR doesn't change any content.

Screenshots

Local build with the Behat Screenshot work page switched to card, desktop at 1280px:

Card banner on a work page, desktop

Phone at 390px, with the card below the intro:

Card banner on a work page, mobile

Summary by CodeRabbit

  • New Features
    • Added a Card banner option for project pages and banner blocks.
    • Card banners display text alongside a featured image, with responsive layout, rounded corners, and a shadow.
    • Card images are optimized for display at up to 960 × 960 pixels while preserving their original proportions.
  • Documentation
    • Updated performance guidance with details about Card banner image sizing and layout.

@AlexSkrypnyk AlexSkrypnyk added A1 Board worker 1 AUTOMERGE Pull request has been approved and set to automerge labels Nov 7, 2026
@coderabbitai

coderabbitai Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

Explain Change →

No actionable comments were generated in the recent review.

Walkthrough

The banner supports a new card type with a dedicated 960×960 WebP image style. Preprocessing selects the effective banner type, and the template renders card banners with a separate image column. Configuration, styles, tests, and performance documentation cover the new type.

Changes: Card Banner

Layer / File(s) Summary
Card type and image style
config/default/field.storage.block_content.field_c_b_banner_type.yml, config/default/field.storage.node.field_c_n_banner_type.yml, config/default/image.style.banner_card.yml, web/themes/custom/drevops/includes/banner.inc
Both banner type fields allow card. The new image style scales images to 960×960 without upscaling, then converts them to WebP. Preprocessing uses the node type unless it is INHERIT, then uses the block type. It selects banner_card for card banners and banner_featured otherwise.
Card banner rendering
web/themes/custom/drevops/components/03-organisms/banner/banner.twig, web/themes/custom/drevops/components/03-organisms/banner/banner.scss, web/themes/custom/drevops/components/variables.components.scss, web/themes/custom/drevops/components/03-organisms/banner/banner.component.yml, web/themes/custom/drevops/components/03-organisms/banner/banner.stories.js
The template renders the card image in a separate column and uses full-width text columns for the card layout. Component styles add responsive card spacing, image sizing, rounded corners, and a shadow. Component metadata and stories list card as a banner type.
Card banner validation and documentation
web/modules/custom/do_base/tests/src/Unit/ImageStyleConfigTest.php, tests/behat/features/banner_card.feature, docs/performance.md
Unit tests check the image style dimensions, effects, and upscale setting. Behat scenarios check type availability and card and default banner output. Performance documentation describes the style and type selection.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature · Severity of issue fixed: Medium

Sequence Diagram(s)

sequenceDiagram
  participant BannerPreprocess as banner.inc
  participant ImageStyle as banner_card
  participant BannerTemplate as banner.twig
  participant Browser
  BannerPreprocess->>ImageStyle: Select style for effective card type
  ImageStyle->>BannerTemplate: Supply styled featured image
  BannerTemplate->>Browser: Render card layout and image
Loading

Suggested reviewers: nsineok

Merge Risk: Minimal · up to 3f736

The card banner change appears mergeable after normal checks; no unresolved issue was identified.

Pre-merge checks: Passed 5

Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title names the main change: a new selectable 'card' banner type that displays the featured image uncropped. The changeset adds the card type to field storage, the banner Twig/SCSS/JS components, …
Linked Issues check Passed Issue #363 acceptance criteria are met by the changes between merge base 4b789dc and head 3f73679.

(a) Selectable card type [#363]: The config diff adds card|Card to field.storage.node.field_c_n_bann…
Out of Scope Changes check Passed All changed files relate to the #363 card banner objective:
- Config: field storage allowed values for node and block banner type; new image.style.banner_card.
- banner.inc: effective type (node value…
Docstring Coverage Passed No eligible functions found in the changed files. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentat…
Full details: Title check

Explanation

The title names the main change: a new selectable 'card' banner type that displays the featured image uncropped. The changeset adds the card type to field storage, the banner Twig/SCSS/JS components, the banner_card image style (resize-only 960x960, WebP), and the banner.inc helper logic. The tests and docs also match. The title also cites issue #363, which the linked issue confirms. The PR objectives mention PR #364, but the title's issue reference is consistent with the linked issue, so this does not affect the verdict.

Full details: Linked Issues check

Explanation

Issue #363 acceptance criteria are met by the changes between merge base 4b789dc and head 3f73679.

(a) Selectable card type [#363]: The config diff adds card|Card to field.storage.node.field_c_n_banner_type and field.storage.block_content.field_c_b_banner_type (per the change summary, not re-read in this review). banner.twig whitelists 'card' in the type check.

(b) Whole image shown, uncropped, desktop and mobile [#363]: For type card with a featured image, banner.twig sets is_card and renders the image in a col-xxs-12 col-m-5 column with no absolute __featured-image__wrapper. The wrapper is rendered only when 'featured_image and not is_card'. banner.scss sets width: 100%, height: auto and display: block on .ct-banner__card-image, with no object-fit or crop. The column stacks below the intro under the m breakpoint, so it is not hidden on mobile.

(c) WebP, at most 960px, through banner_card [#363]: banner.inc passes 'banner_card' to civictheme_media_image_get_variables() when $type === 'card'. The banner_card style is a 960x960 image_scale with upscale off, then image_convert to WebP (per the change summary and ImageStyleConfigTest::testCardImageScalesWithoutCrop, not re-read in this review).

(d) default, large and intro unchanged [#363]: For these types column_class evaluates to the same classes as before (col-xxs-12 col-m-6 with a featured image, col-xxs-12 without). The featured-image wrapper still renders and still uses banner_featured. The inherit change in banner.inc resolves to the block type, which is default in the only current banner block, so no current page changes. This change is required for the card effective-type rule in #363.

Residual uncertainty: #363 asks that the card <img> carry the derivative width and height to reserve layout space. The reviewed diff does not change the dimension handling. The PR description states that do_image_dimensions() supplies these attributes. This was not verified in the reviewed code. It is not a listed acceptance criterion.

Full details: Out of Scope Changes check

Explanation

All changed files relate to the #363 card banner objective:

  • Config: field storage allowed values for node and block banner type; new image.style.banner_card.
  • banner.inc: effective type (node value, or block type when the node says inherit) and selection of banner_card for card. This is required by #363.
  • banner.twig: card whitelist, column_class, card column markup, and the wrapper guard. These implement the desktop and mobile card layout.
  • banner.scss and variables.components.scss: card layout, image, radius and shadow tokens. #363 asks for rounded corners and a shadow.
  • banner.component.yml and banner.stories.js: list card as a type value.
  • ImageStyleConfigTest and banner_card.feature: tests for the new style and card rendering. #363 requests these tests.
  • docs/performance.md: documents banner_card. #363 requests this update.

No unrelated functional change was found in the reviewed diffs. The changes to config, tests, and docs were assessed from the change summary and were not individually re-read.

Full details: Docstring Coverage

Explanation

No eligible functions found in the changed files. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 0 functions across 2 files. (10 skipped: 10 unsupported.)

Finishing Touches

Generate docstrings

  • Commit to this branch
  • Create a new PR
Generate unit tests (beta)

  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Recent review info

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Team
  • Run ID: 23f2da9a-8eb2-4c24-b05e-6fe2e2f0d956
Commits

Reviewing files that changed from the base of the PR and between 4b789dc and 3f73679.

Files selected for processing: 12

  • config/default/field.storage.block_content.field_c_b_banner_type.yml
  • config/default/field.storage.node.field_c_n_banner_type.yml
  • config/default/image.style.banner_card.yml
  • docs/performance.md
  • tests/behat/features/banner_card.feature
  • web/modules/custom/do_base/tests/src/Unit/ImageStyleConfigTest.php
  • web/themes/custom/drevops/components/03-organisms/banner/banner.component.yml
  • web/themes/custom/drevops/components/03-organisms/banner/banner.scss
  • web/themes/custom/drevops/components/03-organisms/banner/banner.stories.js
  • web/themes/custom/drevops/components/03-organisms/banner/banner.twig
  • web/themes/custom/drevops/components/variables.components.scss
  • web/themes/custom/drevops/includes/banner.inc

Included review availability: This review used your included allowance. 1 included review remains after this review. Your included PR review attempts over the past 7 days set your current allowance at 8 reviews per hour.

@github-actions

github-actions Bot commented Nov 7, 2026

Copy link
Copy Markdown

Code coverage (threshold: 80%)

  Classes: 58.82% (20/34)
  Methods: 80.65% (100/124)
  Lines:   89.22% (894/1002)
Per-class coverage
Drupal\do_ai_alt_text\AltTextGenerator
  Methods:  85.71% ( 6/ 7)   Lines:  98.72% ( 77/ 78)
Drupal\do_ai_alt_text\Plugin\Action\RegenerateImageAltText
  Methods: 100.00% ( 8/ 8)   Lines: 100.00% ( 63/ 63)
Drupal\do_base\EventSubscriber\PreviewLinkCacheSubscriber
  Methods:  33.33% ( 1/ 3)   Lines:  11.11% (  1/  9)
Drupal\do_base\EventSubscriber\ThemeColorSubscriber
  Methods:  66.67% ( 2/ 3)   Lines:  40.00% (  4/ 10)
Drupal\do_base\Hook\AutomatedListPagerHook
  Methods: 100.00% ( 5/ 5)   Lines: 100.00% ( 31/ 31)
Drupal\do_base\Hook\LibraryInfoAlterHook
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% ( 11/ 11)
Drupal\do_base\Hook\MetatagsAlterHook
  Methods: 100.00% ( 7/ 7)   Lines: 100.00% ( 58/ 58)
Drupal\do_base\Hook\PageAttachmentsHook
  Methods:  87.50% ( 7/ 8)   Lines:  96.61% ( 57/ 59)
Drupal\do_base\Hook\SvgMediaHook
  Methods:  75.00% ( 3/ 4)   Lines:  96.15% ( 25/ 26)
Drupal\do_base\Hook\TwigValidatorRuleInfoAlterHook
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  3/  3)
Drupal\do_base\NavigationScriptHash
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% ( 27/ 27)
Drupal\do_base\SvgDimensions
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% ( 13/ 13)
Drupal\do_base\Twig\IconAssetsExtension
  Methods: 100.00% ( 2/ 2)   Lines: 100.00% (  3/  3)
Drupal\do_base\Twig\ImageDimensionsExtension
  Methods:  60.00% ( 3/ 5)   Lines:  93.02% ( 40/ 43)
Drupal\do_content_api\EventSubscriber\JsonApiWriteGateSubscriber
  Methods:  66.67% ( 2/ 3)   Lines:  88.89% (  8/  9)
Drupal\do_content_api\Hook\EntityCreateAccessHook
  Methods: 100.00% ( 2/ 2)   Lines: 100.00% ( 12/ 12)
Drupal\do_content_api\Hook\ModerationPolicyHook
  Methods:  50.00% ( 1/ 2)   Lines:  93.75% ( 15/ 16)
Drupal\do_content_api\Routing\RouteSubscriber
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% ( 11/ 11)
Drupal\do_feed\FeedUrlBuilder
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% ( 18/ 18)
Drupal\do_feed\Form\FeedSettingsForm
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% ( 15/ 15)
Drupal\do_feed\Hook\EntityDeleteHook
  Methods:  50.00% ( 1/ 2)   Lines:  92.31% ( 12/ 13)
Drupal\do_feed\Hook\EntityPresaveHook
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% ( 54/ 54)
Drupal\do_feed\Hook\PreprocessParagraphHook
  Methods: 100.00% ( 2/ 2)   Lines: 100.00% ( 14/ 14)
Drupal\do_feed\Hook\PreprocessViewsViewRowRssHook
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  3/  3)
Drupal\do_feed\Hook\ViewsPreViewHook
  Methods:  50.00% ( 1/ 2)   Lines:  96.43% ( 27/ 28)
Drupal\do_generated_content\Generator\CaseMatrix
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% ( 12/ 12)
Drupal\do_generated_content\Generator\RelativeDate
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  1/  1)
Drupal\do_package\Hook\PackageAccessHook
  Methods: 100.00% ( 7/ 7)   Lines: 100.00% ( 27/ 27)
Drupal\do_package\Hook\PackageFileHook
  Methods: 100.00% (11/11)   Lines: 100.00% ( 40/ 40)
Drupal\do_package\Routing\RouteSubscriber
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  5/  5)

@codecov

codecov Bot commented Nov 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.85%. Comparing base (4b789dc) to head (3f73679).

Additional details and impacted files
@@           Coverage Diff            @@
##           develop     #364   +/-   ##
========================================
  Coverage    86.85%   86.85%           
========================================
  Files           35       35           
  Lines          791      791           
========================================
  Hits           687      687           
  Misses         104      104           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@AlexSkrypnyk
AlexSkrypnyk merged commit 50cf857 into develop Nov 7, 2026
15 of 16 checks passed
@AlexSkrypnyk
AlexSkrypnyk deleted the feature/363-card-banner branch November 7, 2026 04:10

This branch was successfully deployed

1 active deployment
PR-364 — 3f736794 Deployed Nov 7, 2026 by AlexSkrypnyk
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A1 Board worker 1 AUTOMERGE Pull request has been approved and set to automerge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a 'card' banner type that shows work page images uncropped

1 participant