Skip to content

Applied the dark redesign across homepage, blog, services and contact.#179

Open
AlexSkrypnyk wants to merge 40 commits into
developfrom
feature/site-redesign
Open

Applied the dark redesign across homepage, blog, services and contact.#179
AlexSkrypnyk wants to merge 40 commits into
developfrom
feature/site-redesign

Conversation

@AlexSkrypnyk

@AlexSkrypnyk AlexSkrypnyk commented Jun 9, 2026

Copy link
Copy Markdown
Member

Summary

Applied a full dark design system to the DrevOps website (Drupal 11 / CivicTheme) across the homepage, blog listing, blog post, services, and contact pages by enhancing CivicTheme's existing components, not by embedding design HTML into content. The dark palette and the Plus Jakarta Sans / Outfit typefaces are ported into CivicTheme's --ct-* custom-property system, and every component renders on the dark palette. Each page is assembled by idempotent deploy hooks that populate CivicTheme component fields (banner, manual list, snippet, promo, callout, webform, rich-text content) - no raw markup is stored as content, and the full_html format is removed. The database can be rebuilt deterministically at any time.

Changes

Dark theme foundations (web/themes/custom/drevops/)

  • components/variables.base.scss / components/variables.components.scss - Replaced the CivicTheme brand and semantic colour maps with the dark design system (deep-navy canvas, cyan interactive accent, coral highlight) and the Plus Jakarta Sans / Outfit type scale, generated into --ct-* custom properties.
  • assets/sass/brand/ - The design layer: _tokens.scss (design tokens), _buttons.scss (the .ct-button outline treatment), _content.scss (rich-text typography for the article, contact details and hero subtitle), _lists.scss (the manual-list section treatments and the service-detail promo), _animations.scss (scroll reveal) and _extra.scss (canvas, chrome and navigation), imported via assets/sass/theme.scss.
  • assets/js/reveal.js - A scroll-reveal behaviour that fades each section component (.ct-list, .ct-promo, .ct-callout) up as it enters the viewport. The hiding class is applied by the script, so sections stay fully visible without JavaScript, without IntersectionObserver, or under reduced-motion.

Component enhancements

  • components/03-organisms/banner/banner.scss and includes/banner.inc - The hero is the CivicTheme intro banner: a deep-navy canvas with an atmospheric glow (the banner inner is transparent so the page background shows through rather than CivicTheme's lighter surface). banner.inc drops the shared banner block's background image, and re-reads the raw banner title so the design's coral accent word survives (CivicTheme strips tags from string fields).
  • components/02-molecules/fact-card/ and templates/paragraphs/paragraph--do-fact-card.html.twig - A new drevops:fact-card component (a large figure above a label) renders the new do_fact_card paragraph type used for the homepage "The essentials" figures, laid out as a bordered grid.
  • includes/snippet.inc - Restores the full snippet summary (CivicTheme trims it to a teaser length) so the homepage service, "why" and process descriptions render in full.
  • includes/paragraphs.inc and includes/manual_list.inc - Map a field_do_style value on a manual list to a .ct-list--style-* modifier (numbered, stat, trust, dotted), so the bespoke sections are styled CSS-only with no stored markup.
  • components/03-organisms/footer/ - Minimal dark footer (copyright + contact email). The footer region slots are still rendered when populated, so the block-demo page sees every declared region while the live footer stays minimal.
  • includes/page.inc / includes/sidebar_navigation.inc - Force page, header, footer and sidebar navigation into the dark theme.

Deploy hooks (web/modules/custom/do_base/do_base.deploy.php)

Idempotent deploy hooks built on the drupal_helpers Helper facade, each staging new components and clearing the banner before deleting the previous paragraphs, so a failed save can never leave a node referencing deleted entities:

  • do_base_deploy_components_dark / do_base_deploy_blocks_dark - Flip every paragraph and block (including list items, the mobile navigation drawer and its trigger) to the dark theme.
  • do_base_deploy_homepage - Banner hero (title, rich-text subtitle, CTA) plus manual lists of snippets (numbered services, stat figures, trust grid, dotted "why", numbered process) and a closing callout.
  • do_base_deploy_services - Banner hero plus three promos (each a rich-text service detail with tagline, description, "what's included" list and price), a dotted "approach" manual list, and a callout.
  • do_base_deploy_contact - Banner hero, the existing contact webform, and the contact details as rich-text content.
  • do_base_deploy_blog_demo - Seeds the demo article with a civictheme_rich_text body (headings, striped tables, highlighted code blocks, blockquotes and a CTA button).

Configuration (config/default/)

  • field.storage.paragraph.field_do_style.yml and field.field.paragraph.civictheme_manual_list.field_do_style.yml - The list-style marker field driving the bespoke manual-list treatments.
  • paragraphs.paragraphs_type.do_fact_card.yml (plus field instances and displays) - The Fact card paragraph type, added to the manual list's allowed list-item bundles.
  • filter.format.full_html.yml / editor.editor.full_html.yml - Removed; all editorial content uses civictheme_rich_text.
  • core.extension.yml - Removed do_generated_content and generated_content (CI enables them; they are not committed config).
  • block.block.drevops_footer_* - Removed the eight footer-region block placements (site branding, social links, four menus, acknowledgment of country, copyright) so the live footer is minimal by configuration.
  • drevops.settings.yml / block.block.drevops_signup.yml - Dark skip link; disabled the site-wide signup CTA block.

Tests (tests/behat/)

  • behat.feature - Footer-region assertions retained: the block-demo page renders every declared region.
  • contact.feature - Heading assertions updated to the redesigned contact hero.

Before / After

BEFORE (light CivicTheme defaults)          AFTER (dark redesign on components)
─────────────────────────────────────────   ─────────────────────────────────────────
Header (light nav bar)                       Header (dark, uppercase Outfit nav)
  Banner (default short hero)                  Banner intro hero (dark + glow)
  Page body (white background)                Page body (deep-navy --ct-* dark tokens)
    Paragraphs (light theme)                    Banner + manual lists + promos + callout
    Generic teaser sections                     populated via component fields
    Raw HTML pasted via full_html               (full_html removed; rich-text only)
  Back-to-top / forms (light accent)          Back-to-top / forms (cyan accent)
  Footer (CivicTheme blocks)                  Footer (minimal: copyright + email)

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds redesign tokens, component styles, two frontend Drupal behaviors (reveal, stats), many static content templates, deploy hooks to seed dark-themed paragraphs from HTML, theme/footer/banner wiring, and small Drupal configuration edits.

Changes

Website Redesign Implementation

Layer / File(s) Summary
Design System Tokens
web/themes/custom/drevops/assets/sass/redesign/_tokens.scss
CSS custom properties define stepped color palettes (primary/secondary/tertiary/neutral/status), font/size/weight/line-height primitives, semantic typography tokens, dark-mode color tokens with color-mix calculations, and responsive display overrides using clamp().
Theme SCSS Variables
web/themes/custom/drevops/components/variables.base.scss, web/themes/custom/drevops/components/variables.components.scss
Base SCSS variables override brand colors, semantic color mappings, typography scale (headings/text/labels), and component accent colors to use new design system tokens.
Interactive Behaviors
web/themes/custom/drevops/assets/js/reveal.js, web/themes/custom/drevops/assets/js/stats-counter.js
Drupal behaviors: reveal.js adds .visible to .component-reveal on viewport intersection; stats-counter.js animates .stat-count elements from 0 to data-target using easing and requestAnimationFrame.
Component Library Styling
web/themes/custom/drevops/assets/sass/redesign/_buttons.scss, web/themes/custom/drevops/assets/sass/redesign/_components.scss, web/themes/custom/drevops/assets/sass/redesign/_extra.scss, web/themes/custom/drevops/assets/sass/theme.scss
Complete .component-* design system: hero variants, typography utilities, grids (stat/trust), lists, service panels, CTA, blog/article styles, contact/steps/forms, navigation, footer, reveal utilities, keyframes, and no-JS reveal fallback.
Page Content Markup
web/modules/custom/do_base/content/blog/demo-article.html, web/modules/custom/do_base/content/contact/info.html, web/modules/custom/do_base/content/homepage/*.html, web/modules/custom/do_base/content/services/*.html
Static HTML markup for blog (CI optimization tips), contact (info/expectations), homepage sections (services/stats/trust/why/process/contact CTA), and services pages (detail/approach/CTA), using redesign classes.
Deploy Hooks for Page Migration
web/modules/custom/do_base/do_base.deploy.php
Drupal deploy hooks batch-update paragraph theme to dark, and rebuild homepage/services/contact/blog-demo by loading sorted HTML markup files, creating dark civictheme_content paragraphs with full_html, and replacing node components idempotently.
Theme Integration: Footer, Preprocessing, Libraries
web/themes/custom/drevops/components/03-organisms/footer/footer.component.yml, web/themes/custom/drevops/components/03-organisms/footer/footer.scss, web/themes/custom/drevops/components/03-organisms/footer/footer.twig, web/themes/custom/drevops/components/03-organisms/banner/banner.scss, web/themes/custom/drevops/includes/banner.inc, web/themes/custom/drevops/includes/page.inc, web/themes/custom/drevops/drevops.info.yml, web/themes/custom/drevops/drevops.libraries.yml, web/themes/custom/drevops/components/04-templates/page/page.twig, tests/behat/features/behat.feature
Footer component metadata, Twig and SCSS implement a minimal dark footer; banner preprocess applies intro-hero classes for front page; page preprocess sets dark theme and supplies site_name/contact_email; redesign JS library is registered; Behat test adjusted for minimal footer rendering.
Configuration and Module Enablement
config/default/block.block.drevops_signup.yml, config/default/core.extension.yml, config/default/filter.format.full_html.yml, web/modules/custom/do_base/do_base.info.yml
Disables signup block, adds do_generated_content/drupal_helpers/generated_content entries (unset), enables Full HTML text format, and declares drupal_helpers as a do_base dependency.

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Applied the dark redesign across homepage, blog, services and contact' accurately summarizes the main changes: a dark design system applied to multiple key pages.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/site-redesign

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

This comment has been minimized.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@web/modules/custom/do_base/content/blog/demo-article.html`:
- Line 158: The anchor with visible text "parallel execution" is a placeholder
(href="#") and must be updated to a real destination or removed; locate the <a>
element around the text "parallel execution" in the paragraph and either replace
href="#" with the correct URL to the relevant docs/article (e.g., PHPUnit
parallel execution/paratest docs) or remove the <a> tag so the text is plain
content, ensuring the final markup contains no non-functional links.

In `@web/modules/custom/do_base/do_base.deploy.php`:
- Around line 183-193: The current _do_base_set_components(Node $node, string
$dir) deletes all existing field_c_n_components entities before calling
_do_base_html_paragraphs($dir); change the flow so you first call
_do_base_html_paragraphs($dir) and validate its return (non-empty, no errors)
and only then delete the referenced entities and set the field; if the function
returns empty/false, do not delete existing components and instead return/LOG a
warning or leave the node unchanged. Ensure you reference the Node object, the
field name field_c_n_components, and the helper _do_base_html_paragraphs when
implementing this check.
- Around line 48-49: Replace the fragile hard-coded Node::load(1) in
do_base_deploy_homepage() with a UUID-based lookup: retrieve the node storage
via the entity type manager and load the node by its UUID (like the Services
deploy hook does), then handle the case where the lookup returns no result
before using the node; update do_base_deploy_homepage() to use that UUID lookup
instead of Node::load(1).
- Around line 123-138: The code deletes existing paragraph components then
creates a webform and appends HTML paragraphs from
_do_base_html_paragraphs('contact') which can return an empty array; instead,
first compute and validate the new components array (call
_do_base_html_paragraphs('contact') and build $components including the new
Paragraph::create result), ensure the resulting $components contains the
required contact details (i.e. more than just the webform or at least meets
whatever non-empty/required-structure check you use), and only if validation
passes, delete existing referenced entities and set field_c_n_components on
$node and save; update references to the existing logic around
Paragraph::create, $components, _do_base_html_paragraphs, and
$node->set('field_c_n_components') accordingly.
- Around line 204-216: The function _do_base_html_paragraphs should validate
that the content directory exists and is readable before calling glob and should
log/readably handle file read failures to avoid silent data loss; update
_do_base_html_paragraphs to check is_dir($path) and is_readable($path) and if
either check fails call \Drupal::logger('do_base')->error(...) and return an
empty array (or throw a clear exception), ensure glob() is only called after
those checks, and when file_get_contents($file) returns FALSE log the filename
and error via \Drupal::logger('do_base')->warning(...) and skip that file
instead of silently continuing; reference _do_base_set_components in tests or
calling code to ensure callers handle the empty/exceptional result
appropriately.
- Around line 218-225: The paragraph creation in _do_base_html_paragraphs()
currently saves raw file_get_contents() HTML into Paragraph::create(...) with
text format 'full_html', which lacks the filter_html sanitizer; update the
deploy code to either (A) use a restricted text format that includes the
filter_html filter when setting field_c_p_content (replace 'full_html' with the
sanitizing format machine name), or (B) add a deploy-time validation step in
_do_base_html_paragraphs() that parses each do_base/content/**/*.html and
rejects or strips forbidden tags/attributes (e.g., <script>, on* attributes)
before saving; also add a short comment/docs note near
_do_base_html_paragraphs() documenting the trust boundary for these partials so
maintainers know only trusted HTML is allowed.

In `@web/themes/custom/drevops/assets/js/stats-counter.js`:
- Around line 39-41: The early return when IntersectionObserver is unavailable
causes counters to stay at their initial values; before returning from the block
that checks if (!elements.length || !('IntersectionObserver' in window)) in
stats-counter.js, iterate over the collected elements and set each element's
displayed value to its data-target (parse numeric values as needed) so the final
stats are shown when the observer isn't supported; use the same attribute name
(data-target) and the same element collection variable (elements) used elsewhere
in the file to locate and update the nodes.

In `@web/themes/custom/drevops/assets/sass/redesign/_components.scss`:
- Line 159: Several padding declarations use horizontal tokens for vertical
spacing (e.g., the padding line with calc(var(--space-y-10) * 1.25)
var(--space-y-3) var(--space-x-10)); update each affected padding shorthand so
the top and bottom values use the Y-axis tokens instead of X-axis tokens.
Specifically, locate the padding properties at the reported spots (including the
shown line and the other occurrences) and replace any top/bottom uses of
--space-x-* with the equivalent --space-y-* token (or appropriate calc using
--space-y-*) so vertical rhythm uses the Y scale.
- Around line 1671-1712: The reveal transitions and keyframe animations
(.component-reveal, .component-reveal.visible, .component-reveal-d1..d6 and
`@keyframes` heroGlow, scrollPulse, fadeUp, fadeIn) run unconditionally; add a
prefers-reduced-motion: reduce media-query override that disables or minimizes
motion by setting transitions and animations to none (or near-zero duration),
removing transform changes and using direct opacity where needed, and ensure the
delay classes (.component-reveal-d1..d6) are also neutralized inside that media
query so motion-sensitive users won't see the animated transforms or delays.

In `@web/themes/custom/drevops/assets/sass/redesign/_tokens.scss`:
- Line 19: Replace loud block comments using /* ... */ with SCSS single-line
comments starting with // throughout the file to satisfy stylelint
scss/comment-no-loud. Specifically update the comment instances like the
"primary" block comment at _tokens.scss (and the other occurrences listed: lines
corresponding to 32, 45, 58, 71, 84, 97, 110, 129, 134, 146, 155, 162, 184, 195,
206, 213, 219, 223, 229, 241, 247, 251, 258, 263, 280, 287, 291, 296) by
converting each /* comment */ into a single-line // comment while preserving the
exact comment text and spacing.

In `@web/themes/custom/drevops/components/03-organisms/footer/footer.scss`:
- Line 41: Replace the legacy max-width media query notation with the
range-context form: locate the media query line containing "`@media` (max-width:
767px)" in footer.scss and change it to the range-context syntax "`@media` (width
<= 767px)"; ensure any matching closing brace remains unchanged so the styles
inside the `@media` block are preserved.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 51f32308-233d-444c-bcbb-755c426a624b

📥 Commits

Reviewing files that changed from the base of the PR and between 237768a and d7b1152.

📒 Files selected for processing (34)
  • config/default/block.block.drevops_signup.yml
  • config/default/core.extension.yml
  • config/default/filter.format.full_html.yml
  • web/modules/custom/do_base/content/blog/demo-article.html
  • web/modules/custom/do_base/content/contact/info.html
  • web/modules/custom/do_base/content/homepage/01-services.html
  • web/modules/custom/do_base/content/homepage/02-stats.html
  • web/modules/custom/do_base/content/homepage/03-trust.html
  • web/modules/custom/do_base/content/homepage/04-why.html
  • web/modules/custom/do_base/content/homepage/05-process.html
  • web/modules/custom/do_base/content/homepage/06-contact.html
  • web/modules/custom/do_base/content/services/01-detail.html
  • web/modules/custom/do_base/content/services/02-approach.html
  • web/modules/custom/do_base/content/services/03-cta.html
  • web/modules/custom/do_base/do_base.deploy.php
  • web/modules/custom/do_base/do_base.info.yml
  • web/themes/custom/drevops/assets/js/reveal.js
  • web/themes/custom/drevops/assets/js/stats-counter.js
  • web/themes/custom/drevops/assets/sass/redesign/_buttons.scss
  • web/themes/custom/drevops/assets/sass/redesign/_components.scss
  • web/themes/custom/drevops/assets/sass/redesign/_extra.scss
  • web/themes/custom/drevops/assets/sass/redesign/_tokens.scss
  • web/themes/custom/drevops/assets/sass/theme.scss
  • web/themes/custom/drevops/components/03-organisms/banner/banner.scss
  • web/themes/custom/drevops/components/03-organisms/footer/footer.component.yml
  • web/themes/custom/drevops/components/03-organisms/footer/footer.scss
  • web/themes/custom/drevops/components/03-organisms/footer/footer.twig
  • web/themes/custom/drevops/components/04-templates/page/page.twig
  • web/themes/custom/drevops/components/variables.base.scss
  • web/themes/custom/drevops/components/variables.components.scss
  • web/themes/custom/drevops/drevops.info.yml
  • web/themes/custom/drevops/drevops.libraries.yml
  • web/themes/custom/drevops/includes/banner.inc
  • web/themes/custom/drevops/includes/page.inc

Comment thread web/modules/custom/do_base/content/blog/demo-article.html Outdated
Comment thread web/modules/custom/do_base/do_base.deploy.php Outdated
Comment thread web/modules/custom/do_base/do_base.deploy.php Outdated
Comment thread web/modules/custom/do_base/do_base.deploy.php Outdated
Comment thread web/modules/custom/do_base/do_base.deploy.php Outdated
Comment thread web/themes/custom/drevops/assets/js/stats-counter.js Outdated
Comment thread web/themes/custom/drevops/assets/sass/brand/_components.scss Outdated
Comment thread web/themes/custom/drevops/assets/sass/brand/_components.scss Outdated
// step 11 = near-black. The brand base colour sits near step 9.
// ══════════════════════════════════════════════════════════════
:root {
/* primary */

@coderabbitai coderabbitai Bot Jun 9, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Replace loud block comments with SCSS single-line comments to satisfy linting.

Stylelint is flagging these /* ... */ comments (scss/comment-no-loud). Convert them to // comments to unblock lint checks (for example, Line 19 and the same pattern throughout this file).

Proposed change pattern
-  /* primary */
+  // primary

Also applies to: 32-32, 45-45, 58-58, 71-71, 84-84, 97-97, 110-110, 129-129, 134-134, 146-146, 155-155, 162-162, 184-184, 195-195, 206-206, 213-213, 219-219, 223-223, 229-229, 241-241, 247-247, 251-251, 258-258, 263-263, 280-280, 287-287, 291-291, 296-296

🧰 Tools
🪛 Stylelint (17.12.0)

[error] 19-19: Expected // for comments instead of /* (scss/comment-no-loud)

(scss/comment-no-loud)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/themes/custom/drevops/assets/sass/redesign/_tokens.scss` at line 19,
Replace loud block comments using /* ... */ with SCSS single-line comments
starting with // throughout the file to satisfy stylelint scss/comment-no-loud.
Specifically update the comment instances like the "primary" block comment at
_tokens.scss (and the other occurrences listed: lines corresponding to 32, 45,
58, 71, 84, 97, 110, 129, 134, 146, 155, 162, 184, 195, 206, 213, 219, 223, 229,
241, 247, 251, 258, 263, 280, 287, 291, 296) by converting each /* comment */
into a single-line // comment while preserving the exact comment text and
spacing.

Source: Linters/SAST tools

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These comments are ported verbatim from the design source. The partials under assets/sass/redesign are intentionally outside the theme stylelint scope, so the comment style is preserved for traceability back to the design.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AlexSkrypnyk, understood — makes sense to keep the verbatim comment style for design-source traceability. I'll note that for future reviews.


✏️ Learnings added
Learnt from: AlexSkrypnyk
Repo: drevops/website PR: 179
File: web/themes/custom/drevops/assets/sass/redesign/_tokens.scss:19-19
Timestamp: 2026-06-09T06:26:57.430Z
Learning: In the `drevops` theme (Drupal 11 / CivicTheme), the SCSS partials under `web/themes/custom/drevops/assets/sass/redesign/` are intentionally outside the theme's Stylelint scope. Block-style `/* */` comments in those files are preserved verbatim from the design source for traceability back to the original design variables, so `scss/comment-no-loud` violations there should not be flagged.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/behat/features/contact.feature`:
- Line 13: The step `And I should see "Contact"` is too broad and may match
unrelated text; scope it to the contact webform container instead. Change the
Behat step in the feature to assert the title within the contact webform element
(e.g. use a step like `And I should see "Contact" within
"[data-webform-id='contact']"` or within the webform CSS class such as
`.webform-submission-form`), referencing the contact webform defined in
config/default/webform.webform.contact.yml and the deployment that adds the
civictheme_webform via do_base_deploy_contact() so the check is limited to the
actual webform container.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: cc6df3a2-ca14-4f77-8d4f-14bc8d7c8ec1

📥 Commits

Reviewing files that changed from the base of the PR and between 7f58244 and 8b7425d.

📒 Files selected for processing (1)
  • tests/behat/features/contact.feature

When I go to "/contact"
Then I should see the heading Contact
Then I should see the heading "Let's talk about your platform."
And I should see "Contact"

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assertion verifies the contact webform title Contact renders on the page, which passes. The only Contact heading on the page is the webform title, so the page-level check is unambiguous and kept for simplicity.

@AlexSkrypnyk AlexSkrypnyk temporarily deployed to PR-179 June 9, 2026 06:24 Inactive
…ont page from config, added reduced-motion and stats fallbacks.
When I go to "/contact"
Then I should see the heading Contact
Then I should see the heading "Let's talk about your platform."
And I should see "Contact"

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assertion verifies the contact webform title Contact renders on the page, which passes. The only Contact heading on the page is the webform title, so the page-level check is unambiguous and kept for simplicity.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/modules/custom/do_base/do_base.deploy.php (1)

129-140: ⚠️ Potential issue | 🟠 Major

Prevent contact page component data loss if replacement webform paragraph save fails.
do_base_deploy_contact() deletes existing field_c_n_components entities before creating/saving the replacement $webform; if $webform->save() fails/throws, the existing components are already gone and $node->save() won’t run. Build/save the replacement first, then delete old components and set the field.

Suggested fix
-  foreach ($node->get('field_c_n_components')->referencedEntities() as $existing) {
-    $existing->delete();
-  }
-
   $webform = Paragraph::create([
     'type' => 'civictheme_webform',
     'field_c_p_theme' => 'dark',
     'field_c_p_webform' => 'contact',
   ]);
   $webform->save();
 
   $components = [$webform, ...$details];
+
+  foreach ($node->get('field_c_n_components')->referencedEntities() as $existing) {
+    $existing->delete();
+  }
+
   $node->set('field_c_n_components', $components);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/modules/custom/do_base/do_base.deploy.php` around lines 129 - 140, In
do_base_deploy_contact(), avoid deleting existing field_c_n_components before
creating the replacement: first construct and save the new Paragraph ($webform)
and verify save succeeded, then delete each existing referenced entity
($existing) from $node->get('field_c_n_components')->referencedEntities(), build
the $components array (with the new $webform and $details) and assign it to the
node field, and finally call $node->save(); this ensures existing components are
preserved if $webform->save() fails.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@web/modules/custom/do_base/do_base.deploy.php`:
- Around line 198-203: In _do_base_set_components() avoid deleting referenced
paragraph entities before the node's new component references are persisted;
instead set the new references via $node->set('field_c_n_components',
$components) and call $node->save() to persist the node first, then iterate over
the old referenced entities and call $existing->delete(); alternatively
implement a transactional/rollback strategy around creating paragraphs, setting
the field, saving the node, and deleting old paragraphs so the operation is
atomic and cannot leave dangling references.

---

Outside diff comments:
In `@web/modules/custom/do_base/do_base.deploy.php`:
- Around line 129-140: In do_base_deploy_contact(), avoid deleting existing
field_c_n_components before creating the replacement: first construct and save
the new Paragraph ($webform) and verify save succeeded, then delete each
existing referenced entity ($existing) from
$node->get('field_c_n_components')->referencedEntities(), build the $components
array (with the new $webform and $details) and assign it to the node field, and
finally call $node->save(); this ensures existing components are preserved if
$webform->save() fails.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7778c0fd-16e9-4a79-93d1-e2f0a1074f6b

📥 Commits

Reviewing files that changed from the base of the PR and between 8b7425d and a434a3d.

📒 Files selected for processing (4)
  • web/modules/custom/do_base/content/blog/demo-article.html
  • web/modules/custom/do_base/do_base.deploy.php
  • web/themes/custom/drevops/assets/js/stats-counter.js
  • web/themes/custom/drevops/assets/sass/redesign/_extra.scss

Comment thread web/modules/custom/do_base/do_base.deploy.php Outdated
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
web/modules/custom/do_base/do_base.deploy.php (2)

141-154: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Contact component replacement is non-atomic and can lose content

Existing components are deleted before the new references are persisted on the node. If anything fails before Line 154 save completes, the node can be left with deleted references and the new paragraphs orphaned.

Persist new references first, then delete old references (or wrap the whole sequence in a transaction).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/modules/custom/do_base/do_base.deploy.php` around lines 141 - 154, The
current flow deletes existing paragraph entities via the
$node->get('field_c_n_components')->referencedEntities() loop before persisting
the new Paragraph (Paragraph::create / $webform->save()) and updating the node
($node->set / $node->save()), which risks leaving the node with missing
references if something fails; change the sequence so you first create and save
the new Paragraphs (e.g., using Paragraph::create and $webform->save()), update
the node's field with the new references ($node->set('field_c_n_components',
...)) and save the node ($node->save()), and only after that delete the old
referenced entities ($existing->delete()), or alternatively wrap the
create/save/delete sequence in a transaction so the entire operation is atomic.

50-52: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Resolve aliased front-page paths before parsing node ID

This only works when page.front is already /node/{id}. If it is configured as an alias (for example /home), homepage rebuild is skipped incorrectly.

Proposed fix
-  $front = (string) \Drupal::config('system.site')->get('page.front');
-  $node = preg_match('#^/node/(\d+)$#', $front, $matches) ? Node::load((int) $matches[1]) : NULL;
+  $front = (string) \Drupal::config('system.site')->get('page.front');
+  $internal_path = \Drupal::service('path_alias.manager')->getPathByAlias($front);
+  $node = preg_match('#^/node/(\d+)$#', $internal_path, $matches)
+    ? Node::load((int) $matches[1])
+    : NULL;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/modules/custom/do_base/do_base.deploy.php` around lines 50 - 52, The code
reads the raw page.front value and only matches literal /node/{id}; update the
logic to resolve aliases first using Drupal's path alias service (e.g.
\Drupal::service('path_alias.manager') or
\Drupal::service('path.alias_manager')->getPathByAlias) to get the internal
path, then parse that resolved path for ^/node/(\d+)$ and call
Node::load((int)$matches[1]) as before; replace the current direct use of
\Drupal::config('system.site')->get('page.front') with alias resolution before
preg_match so aliased front pages (like /home) correctly map to the node ID.
♻️ Duplicate comments (1)
web/modules/custom/do_base/do_base.deploy.php (1)

210-215: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

_do_base_set_components() still deletes old references before node save

This is the same unresolved delete-before-persist risk previously flagged: old paragraphs are removed before the caller saves the updated node, so a failed save can leave dangling references/data loss.

Safer sequence
 function _do_base_set_components(Node $node, string $dir): void {
   if (!$node->hasField('field_c_n_components')) {
     return;
   }

   $components = _do_base_html_paragraphs($dir);
+  $old_components = $node->get('field_c_n_components')->referencedEntities();

-  foreach ($node->get('field_c_n_components')->referencedEntities() as $existing) {
-    $existing->delete();
-  }
-
   $node->set('field_c_n_components', $components);
+  $node->save();
+
+  foreach ($old_components as $existing) {
+    $existing->delete();
+  }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/modules/custom/do_base/do_base.deploy.php` around lines 210 - 215, The
_do_base_set_components() function currently deletes existing paragraph entities
via iterating referencedEntities() on $node->get('field_c_n_components') before
the node is saved, which risks data loss on failed saves; change the sequence so
you first record the existing referenced entity IDs, set the node's
'field_c_n_components' to $components (replacing references) and let the caller
save the node, then after a successful save delete only the previously-recorded
paragraph entities that are no longer referenced; in short: do not call delete()
on $existing before persistence — collect IDs, replace the field on $node, and
perform deletions only post-save (or return the IDs so the caller can delete
them after save).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@web/modules/custom/do_base/do_base.deploy.php`:
- Around line 84-89: The banner cleanup in _do_base_clear_banner() currently
deletes referenced paragraph entities immediately (using
$node->get($field)->referencedEntities() and $existing->delete()) before the
node is saved, which can leave dangling references if the save fails; change the
flow to capture the old referenced entities into a temporary array, set or stage
the new value on the node (e.g. $node->set($field, []) or assign the new
references), call $node->save() to persist the pointer changes, and only after a
successful save loop over the previously captured $oldReferences to delete each
$existing; update the function to follow this "stage -> save node -> delete old
entities" pattern.

In `@web/themes/custom/drevops/assets/sass/redesign/_extra.scss`:
- Around line 33-49: The selector .ct-basic-content:has(section) is too broad
and can remove layout constraints on non-redesign pages; narrow it to target
only redesign markup by changing the selector to require the redesign section
marker (e.g., use .ct-basic-content:has(.component-section) or
.ct-basic-content.component-section:has(section)) so the contained rules for
.container, .row and [class*='col-'] only apply to redesign sections; update the
selector in _extra.scss accordingly.

---

Outside diff comments:
In `@web/modules/custom/do_base/do_base.deploy.php`:
- Around line 141-154: The current flow deletes existing paragraph entities via
the $node->get('field_c_n_components')->referencedEntities() loop before
persisting the new Paragraph (Paragraph::create / $webform->save()) and updating
the node ($node->set / $node->save()), which risks leaving the node with missing
references if something fails; change the sequence so you first create and save
the new Paragraphs (e.g., using Paragraph::create and $webform->save()), update
the node's field with the new references ($node->set('field_c_n_components',
...)) and save the node ($node->save()), and only after that delete the old
referenced entities ($existing->delete()), or alternatively wrap the
create/save/delete sequence in a transaction so the entire operation is atomic.
- Around line 50-52: The code reads the raw page.front value and only matches
literal /node/{id}; update the logic to resolve aliases first using Drupal's
path alias service (e.g. \Drupal::service('path_alias.manager') or
\Drupal::service('path.alias_manager')->getPathByAlias) to get the internal
path, then parse that resolved path for ^/node/(\d+)$ and call
Node::load((int)$matches[1]) as before; replace the current direct use of
\Drupal::config('system.site')->get('page.front') with alias resolution before
preg_match so aliased front pages (like /home) correctly map to the node ID.

---

Duplicate comments:
In `@web/modules/custom/do_base/do_base.deploy.php`:
- Around line 210-215: The _do_base_set_components() function currently deletes
existing paragraph entities via iterating referencedEntities() on
$node->get('field_c_n_components') before the node is saved, which risks data
loss on failed saves; change the sequence so you first record the existing
referenced entity IDs, set the node's 'field_c_n_components' to $components
(replacing references) and let the caller save the node, then after a successful
save delete only the previously-recorded paragraph entities that are no longer
referenced; in short: do not call delete() on $existing before persistence —
collect IDs, replace the field on $node, and perform deletions only post-save
(or return the IDs so the caller can delete them after save).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 36621c92-db3b-4157-97bf-4cfc8a9df18a

📥 Commits

Reviewing files that changed from the base of the PR and between a434a3d and 6afdf11.

📒 Files selected for processing (9)
  • web/modules/custom/do_base/content/contact/00-hero.html
  • web/modules/custom/do_base/content/homepage/00-hero.html
  • web/modules/custom/do_base/content/homepage/01-services.html
  • web/modules/custom/do_base/content/homepage/05-process.html
  • web/modules/custom/do_base/content/services/00-hero.html
  • web/modules/custom/do_base/content/services/01-detail.html
  • web/modules/custom/do_base/do_base.deploy.php
  • web/themes/custom/drevops/assets/sass/redesign/_extra.scss
  • web/themes/custom/drevops/components/variables.base.scss

Comment thread web/modules/custom/do_base/do_base.deploy.php Outdated
Comment thread web/themes/custom/drevops/assets/sass/redesign/_extra.scss Outdated
@AlexSkrypnyk AlexSkrypnyk temporarily deployed to PR-179 June 9, 2026 07:13 Inactive
@github-actions

This comment has been minimized.

@AlexSkrypnyk AlexSkrypnyk temporarily deployed to PR-179 June 9, 2026 07:27 Inactive
…scoped the container reset to redesign sections.
@github-actions

This comment has been minimized.

@AlexSkrypnyk AlexSkrypnyk temporarily deployed to PR-179 June 9, 2026 07:41 Inactive
@AlexSkrypnyk AlexSkrypnyk temporarily deployed to PR-179 June 9, 2026 22:10 Inactive
The hero was washed out because the CivicTheme banner surface sits lighter than the design's deep-navy canvas. The banner inner is now transparent so the page background shows through, and the headline accent word renders in the design's coral - the banner preprocess re-reads the raw title (CivicTheme strips tags from string fields) so the accent markup survives. The services promo 'What's included' label now uses the coral accent too.

The homepage 'The essentials' figures are now a dedicated do_fact_card paragraph type (a large figure above a label) rendered through a new drevops:fact-card component and referenced from the manual list, replacing the stat snippets. They lay out as a bordered grid on the subtle surface.
@github-actions

This comment has been minimized.

@AlexSkrypnyk AlexSkrypnyk requested a deployment to PR-179 June 9, 2026 23:08 Abandoned
CivicTheme's responsive navigation rules were not reaching the compiled theme CSS, so the horizontal desktop menu and the hamburger trigger both showed at every width. Restored the design's behaviour - a horizontal menu on desktop and a hamburger on mobile - switching at the 768px desktop-menu breakpoint.
@github-actions

This comment has been minimized.

@AlexSkrypnyk AlexSkrypnyk temporarily deployed to PR-179 June 9, 2026 23:45 Inactive
Added a 'field_do_eyebrow' field to the manual list so each section can carry the design's small coral category label (What we do, The essentials, Who we work with, How we work) above its title. The manual list template renders it, and eyebrow-only sections leave the title empty.

The trust grid used CivicTheme's four-column flex layout, where the content-box snippets overflowed and overlapped. It now uses the design's clean CSS grid with border-box cells - four columns with a gap, two on mobile.

The homepage CTA email rendered as a CivicTheme external button (new-window target, arrow, 'opens in a new tab' text). It is now a plain large link in the callout body, matching the design's email link.
@github-actions

This comment has been minimized.

@AlexSkrypnyk AlexSkrypnyk temporarily deployed to PR-179 June 10, 2026 00:08 Inactive
Reworked the homepage copy for v2: a new hero eyebrow and subtitle, reworded service and process descriptions, a new 'AI-assisted delivery' section (a dotted list of three points), refreshed statistics, and the 'Track record' section label. The other pages are unchanged between v1 and v2.

Added a 'field_do_suffix' field to the Fact card so a figure can carry a small trailing unit (1 'day', 10 'yrs', 40 '+'), rendered smaller beside the number. Added the hero eyebrow above the headline.
@github-actions

This comment has been minimized.

@AlexSkrypnyk AlexSkrypnyk temporarily deployed to PR-179 June 10, 2026 01:43 Inactive
Removed the primary-navigation side menu block from the sidebar so the blog post is a single column with no left sidebar, and widened the article measure to the design's 1100px so the content fills the container.
@github-actions

This comment has been minimized.

@AlexSkrypnyk AlexSkrypnyk temporarily deployed to PR-179 June 10, 2026 03:53 Inactive
@github-actions

This comment has been minimized.

@AlexSkrypnyk AlexSkrypnyk temporarily deployed to PR-179 June 10, 2026 04:27 Inactive
@github-actions

Copy link
Copy Markdown

Code coverage (threshold: 80%)

  Classes: 50.00% (5/10)
  Methods: 73.91% (17/23)
  Lines:   92.88% (313/337)
Per-class coverage
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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Needs review Pull request needs a review from assigned developers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant