Acquia FE Specialist (D10) common mistakes
Acquia FE Specialist (D10) · Understand the traps and how to avoid them.
Writing a preprocess function that builds a render array but then printing it with {{ variables.markup }} and expecting it to be rendered, or conversely building a string when a render array is required.
Candidates conflate Twig output with PHP render arrays; they learn that Twig prints values and assume a render array can be echoed directly, leading to 'Array to string conversion' notices or missing markup.
What to do instead
In preprocess, pass structured data as render arrays or plain scalars; in Twig, print render arrays with {{ content.field_name }} (or a Twig variable holding a render array) rather than {{ some_array }}, and use |without or attribute access only on render arrays that are actually renderable.
Using the raw field value from a preprocess function (e.g. $variables['node']->get('field_x')->value) and printing it in Twig without applying the appropriate sanitization or render pipeline.
Candidates are used to raw database values and do not realize that bypassing the field formatter and the render system can output unescaped or unformatted content, which is both a security and a correctness problem.
What to do instead
Prefer printing the field's render array from content (e.g. {{ content.field_x }}) so formatters and XSS filtering apply; if a raw value is genuinely needed, pass it through a formatter or apply |escape (or use Twig autoescape, which is on by default) and document why the render pipeline is bypassed.
Overriding a template by copying it into the theme with the same base name (e.g. node.html.twig) when a more targeted suggestion is needed, or guessing at suggestion names that Drupal never generates.
Candidates know template overrides exist but not the suggestion naming convention or how to discover the actual suggestions; they copy the generic file and then wonder why their change applies everywhere or nowhere.
What to do instead
Enable Twig debug (twig.config debug: true) and read the HTML comments listing suggestions, then name the file exactly as suggested (e.g. node--article--teaser.html.twig); use hook_theme_suggestions_HOOK_alter() to add custom suggestions when needed. Do not assume suggestion names without verifying them in the debug output.
Assuming a base theme's CSS or JS library is automatically attached when the sub-theme overrides a template, or attaching libraries in the wrong place (e.g. in the .info.yml file or a preprocess that runs too late).
Candidates confuse the theme inheritance of template files with the inheritance of asset libraries; libraries are declared in *.libraries.yml and must be attached explicitly or via a parent library declaration.
What to do instead
Declare libraries in the theme's *.libraries.yml and attach them with {{ attach_library('theme/library') }} in Twig, or via a preprocess function using $variables['#attached']['library']. To inherit a base theme library, list it under the sub-theme's libraries in *.info.yml or attach the base library name explicitly.
Putting logic in Twig templates (complex conditionals, loops computing values, arithmetic) instead of in preprocess functions, or conversely putting all presentation markup into PHP.
Candidates are tempted to solve problems where the data is visible (the template) and do not appreciate the separation of concerns Drupal theming expects, or they overcorrect and bury markup in PHP.
What to do instead
Keep Twig for presentation: prepare and normalize data in hook_preprocess_HOOK() (or a theme's .theme file), then use simple conditionals and loops in Twig. Extract reusable markup into include/embed/extends rather than duplicating logic.
Using {% include %} with variables that are not passed explicitly, expecting the included template to see the parent's context, or misusing {% embed %} vs {% extends %}.
Candidates carry assumptions from other templating languages about scope; Twig includes do not automatically receive the parent context unless 'with context' is used, and embed vs extends have different inheritance semantics.
What to do instead
Pass variables explicitly: {% include 'x.html.twig' with {'foo': bar} only %} (or omit 'only' to inherit context). Use {% embed %} when overriding blocks of an included template, and {% extends %} only for full template inheritance with block overrides.
Writing selectors or markup that fail accessibility basics: images without alt text, form inputs without labels, non-semantic divs for buttons, or color contrast left unchecked.
Candidates focus on visual fidelity and Drupal mechanics and treat accessibility as a separate audit step rather than a property of the markup they author.
What to do instead
Use semantic elements (button, nav, label, img with alt), associate labels with inputs via for/id, ensure focus states are visible and not removed, and check contrast against WCAG AA (4.5:1 for normal text). In Drupal, use the appropriate form/render elements so core markup and attributes are emitted correctly.
Disabling or removing CSS/JS aggregation and caching to 'fix' a styling problem, or leaving render cache enabled while debugging and concluding the template override is not working.
Candidates hit stale-cache symptoms and change global performance settings instead of clearing the right caches, which masks the real issue and degrades performance.
What to do instead
Clear caches (drush cr) after template/preprocess changes; enable Twig debug and disable the Twig cache only in local development (services.yml twig.config debug/auto_reload), and restore aggregation and caching for the performance-related questions. Do not ship with debug or aggregation disabled.
Misunderstanding render cache metadata: assuming a block or page is cached when it is not, or not adding cache tags/contexts when output depends on user or entity data.
Candidates know caching exists but not the tag/context/max-age model, so they either over-invalidate or produce stale personalized output.
What to do instead
Add cacheability metadata to render arrays: '#cache' => ['tags' => [...], 'contexts' => ['user.permissions'], 'max-age' => ...]. Use cache contexts for variations (e.g. user.roles) and cache tags for entity dependencies; let the render system bubble metadata rather than manually clearing caches.
Confusing the roles of a base theme, a sub-theme, and a custom module: putting business logic or field storage in the theme, or expecting a theme to alter data structures.
Candidates learn that themes can preprocess and alter markup and overextend that to data/behavior changes, which belong in modules; themes should not define content models or persistent configuration.
What to do instead
Keep content types, fields, and business logic in custom modules; use the theme for templates, preprocess, libraries, and presentation. When altering data, do it in a module (hooks, plugins, event subscribers) and let the theme render the result.