CivicTheme Upgrade Skill
Assists with planning and executing CivicTheme upgrades in Drupal projects using a documentation-first, sequential upgrade approach.
Core Principles
- •Sequential upgrades: One CivicTheme release per upgrade step. Never skip versions.
- •Exact version constraints: Use
drupal/civictheme:1.12.0not^1.12. - •Non-production first: All work in feature branches, dev, or staging environments.
- •Preserve
.gitignore: Never modify existing.gitignorefiles. - •Customisation register: Track all theme customisations with stable IDs (C001, C002, etc.).
Workflow Overview
0. Pre-flight → 1. Discovery → 2. Planning → 3. Changes → 4. Validation
Step 0: Pre-flight (Baseline Normalisation)
Before any upgrade, ensure exact version pinning and detect parent-theme modifications.
1. Get versions and locate theme:
CIVICTHEME_VERSION=$(composer show drupal/civictheme --locked --format=json | grep -o '"version": "[^"]*"' | head -1 | cut -d'"' -f4) INSTALLED_THEME=$(composer show --path drupal/civictheme) echo "Version: $CIVICTHEME_VERSION | Path: $INSTALLED_THEME" # Check constraint type in composer.json grep -A2 '"drupal/civictheme"' composer.json
Constraint classification: Exact (1.12.0) = OK. Non-exact (^1.12, ~1.12, *) = must normalise after comparison.
2. Detect Composer patches (these are legitimate customisations):
# Check for patches in composer.json grep -A50 '"patches"' composer.json | grep -A10 '"drupal/civictheme"' || echo "No patches" # Check for external patches file grep '"patches-file"' composer.json
Record any patches found – they must be applied to the pristine copy for fair comparison.
3. Build pristine copy (with same patches):
WORKDIR="/tmp/civictheme-compare" rm -rf "$WORKDIR" && mkdir -p "$WORKDIR/pristine-project" cp -R "$INSTALLED_THEME" "$WORKDIR/installed" cd "$WORKDIR/pristine-project" composer init --no-interaction --name="temp/check" # If patches exist: add cweagans/composer-patches and copy patch config composer require drupal/civictheme:$CIVICTHEME_VERSION --no-interaction PRISTINE=$(composer show --path drupal/civictheme) cp -R "$PRISTINE" "$WORKDIR/pristine" cd -
4. Portable diff (remove noisy dirs from copies first):
rm -rf "$WORKDIR/installed/node_modules" "$WORKDIR/pristine/node_modules" rm -rf "$WORKDIR/installed/.npm" "$WORKDIR/pristine/.npm" rm -rf "$WORKDIR/installed/storybook-static" "$WORKDIR/pristine/storybook-static" # NOTE: Do NOT remove dist/ by default – compiled asset differences ARE meaningful diff -rq "$WORKDIR/pristine" "$WORKDIR/installed"
If differences found (after patch-aware comparison): Parent theme manually modified. Backup, record as HIGH risk, STOP for developer decision.
If no differences and non-exact constraint:
composer require drupal/civictheme:<VERSION_FROM_LOCK> --no-update
Step 1: Discovery
Identify current state and customisations:
# Current CivicTheme version (use --locked for authoritative source)
composer show drupal/civictheme --locked | grep -E "^versions"
# Drupal core version (1.11+ requires ^10.2 || ^11)
drush status --field=drupal-version
# Find sub-theme location
ls -la web/themes/custom/
# Audit Twig templates for breaking patterns
grep -rn "include '@atoms/" <subtheme>/templates/
grep -rn "{% extends " <subtheme>/templates/
grep -rn "_slot %}" <subtheme>/templates/
Step 2: Planning
Read version-specific documentation:
references/versions/v<FROM>-to-v<TO>/ ├── spec.md # What & why (upstream changes, risks) ├── tasks.md # Checklist (tickable items) └── playbook.md # How (ordered runbook)
Cross-reference with references/customisations.md to identify impacted customisations.
Step 3: Changes
Apply upgrade in order:
- •Composer update:
composer require drupal/civictheme:<VERSION> - •Twig syntax: Update include patterns
- •Block names: Rename
_slot→_block - •Library overrides: Update file references in
<subtheme>.info.yml - •Build tooling: Update
package.json,build.js, Storybook config
Step 4: Validation
drush cr && drush updb && drush cim -y composer show drupal/civictheme --locked | grep -E "^versions" # Verify exact version npm run build # or ahoy fe
Success criteria: Version output matches exact target version (e.g., 1.12.0).
Test: home page, navigation, search, forms, custom components.
Version-Specific References
| Upgrade Path | Key Changes | Reference |
|---|---|---|
| 1.10.0 → 1.11.0 | SDC migration, Twig namespace changes | references/versions/v1.10.0-to-v1.11.0/ |
| 1.11.0 → 1.12.0 | Security fixes, SDC refinements | references/versions/v1.11.0-to-v1.12.0/ |
| 1.12.0 → 1.12.1 | Patch release | references/versions/v1.12.0-to-v1.12.1/ |
| 1.12.1 → 1.12.2 | Patch release | references/versions/v1.12.1-to-v1.12.2/ |
Before starting any upgrade, read the relevant spec.md and tasks.md for that version step.
Critical Breaking Changes (1.11.0+)
Twig Include Syntax
{# OLD (pre-1.11) #}
{% include '@atoms/paragraph/paragraph.twig' %}
{% include '@molecules/logo/logo.twig' %}
{# NEW (1.11+) #}
{% include 'civictheme:paragraph' %}
{% include 'civictheme:logo' %}
Block Naming
{# OLD #}
{% block content_slot %}
{# NEW #}
{% block content_block %}
Template Extension
Not supported in 1.11+: {% extends %} and {{ parent() }} for CivicTheme components.
Options:
- •Override completely: Copy full upstream template, apply customisations inline
- •Remove override: Use upstream component unchanged
Library Overrides (<subtheme>.info.yml)
libraries-override:
civictheme/global:
css:
theme:
dist/civictheme.base.css: dist/styles.base.css
dist/civictheme.theme.css: dist/styles.theme.css
dist/civictheme.variables.css: dist/styles.variables.css
js:
dist/civictheme.drupal.base.js: dist/scripts.drupal.base.js
Customisation Register
Maintain at docs/civic-theme-upgrades/customisations.md with:
- [ ] C001 [HIGH] Custom header override - templates/civictheme-header.html.twig - [ ] C002 [MEDIUM] Event listing styles - scss/components/_event-card.scss - [ ] C003 [LOW] Footer logo swap - templates/civictheme-footer.html.twig
Impact levels:
- •HIGH: Extended templates, structural changes
- •MEDIUM: Style overrides, custom components
- •LOW: Minor tweaks, configuration
Stop Conditions
Halt and seek developer input when:
- •CivicTheme version doesn't match expected "from" version
- •Drupal core < 10.2 (for 1.11+ upgrades)
- •
{% extends %}patterns found (requires refactoring decision) - •Build failures after tooling updates
- •Test regressions detected
- •Parent theme modified: Differs from pristine after applying same Composer patches
- •Dev/non-release version:
composer.lockshowsdev-*or non-semver version - •Composer patches exist: Record patches in register; ensure they're applied in pristine comparison
DO NOT:
- •Run
composer updateglobally—only update CivicTheme deliberately - •Modify
web/themes/contrib/civicthemedirectly—treat as overwriteable - •Run
npm installinsideweb/themes/contrib/civictheme—masks real diffs - •Flag diff as "modification" without applying same patches to pristine
Additional References
- •
references/planning.md- Global framework and governance - •
references/customisations.md- Customisation register template - •
references/versions/*/spec.md- Version-specific specifications - •
references/versions/*/tasks.md- Version-specific task checklists - •
references/versions/*/playbook.md- Version-specific runbooks
External Links
- •CivicTheme docs: https://docs.civictheme.io
- •CivicTheme releases: https://www.drupal.org/project/civictheme/releases
- •Upgrade tools: https://github.com/civictheme/upgrade-tools