Initial commit
This commit is contained in:
11
.claude-plugin/plugin.json
Normal file
11
.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "drupal-frontend",
|
||||
"description": "Drupal Front End Specialist skill for theme development, Twig templates, and rendering system (Drupal 8-11+). Use when working with Drupal themes, Twig syntax, preprocessing, CSS/JS libraries, or template suggestions.",
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "Drupal Skills Team"
|
||||
},
|
||||
"skills": [
|
||||
"./"
|
||||
]
|
||||
}
|
||||
3
README.md
Normal file
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# drupal-frontend
|
||||
|
||||
Drupal Front End Specialist skill for theme development, Twig templates, and rendering system (Drupal 8-11+). Use when working with Drupal themes, Twig syntax, preprocessing, CSS/JS libraries, or template suggestions.
|
||||
430
SKILL.md
Normal file
430
SKILL.md
Normal file
@@ -0,0 +1,430 @@
|
||||
---
|
||||
name: drupal-frontend
|
||||
description: Drupal Front End Specialist skill for theme development, Twig templates, and rendering system (Drupal 8-11+). Use when working with Drupal themes, Twig syntax, preprocessing, CSS/JS libraries, or template suggestions.
|
||||
---
|
||||
|
||||
# Drupal Front End Development
|
||||
|
||||
## Overview
|
||||
|
||||
Enable expert-level Drupal front end development capabilities. Provide comprehensive guidance for theme development, Twig templating, preprocessing, responsive design, and asset management for Drupal 8, 9, 10, and 11+.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
Invoke this skill when working with:
|
||||
- **Theme development**: Creating or customizing Drupal themes
|
||||
- **Twig templates**: Writing or modifying .twig template files
|
||||
- **Preprocessing**: Implementing preprocess functions for templates
|
||||
- **Template suggestions**: Adding custom template naming patterns
|
||||
- **CSS/JS libraries**: Managing theme assets and dependencies
|
||||
- **Responsive design**: Implementing breakpoints and mobile-first design
|
||||
- **Rendering system**: Understanding Drupal's rendering pipeline
|
||||
- **Theme hooks**: Implementing theme-related hooks and alterations
|
||||
|
||||
## Core Capabilities
|
||||
|
||||
### 1. Theme Development
|
||||
|
||||
Build complete, standards-compliant Drupal themes with proper structure:
|
||||
|
||||
**Quick start workflow:**
|
||||
1. Use theme template from `assets/theme-template/` as starting point
|
||||
2. Replace `THEMENAME` with your theme's machine name
|
||||
3. Replace `THEMELABEL` with human-readable name
|
||||
4. Customize templates, CSS, JS, and libraries
|
||||
5. Enable theme and configure via Appearance UI
|
||||
|
||||
**Theme components:**
|
||||
- `.info.yml` - Theme metadata and configuration
|
||||
- `.libraries.yml` - CSS/JS library definitions
|
||||
- `.theme` - Preprocess functions and theme logic
|
||||
- `.breakpoints.yml` - Responsive breakpoints
|
||||
- `templates/` - Twig template files
|
||||
- `css/` - Stylesheets
|
||||
- `js/` - JavaScript files
|
||||
|
||||
**Reference documentation:**
|
||||
- `references/theming.md` - Complete theming guide with examples
|
||||
|
||||
### 2. Twig Template System
|
||||
|
||||
Master Twig syntax and Drupal-specific extensions:
|
||||
|
||||
**Twig fundamentals:**
|
||||
- `{{ variable }}` - Print variables
|
||||
- `{% if/for/set %}` - Control structures and logic
|
||||
- `{# comment #}` - Comments and documentation
|
||||
- `{{ var|filter }}` - Apply filters to variables
|
||||
- `{% extends %}` - Template inheritance
|
||||
- `{% block %}` - Define overridable sections
|
||||
|
||||
**Drupal-specific Twig:**
|
||||
- `{{ 'Text'|t }}` - Translate strings
|
||||
- `{{ attach_library('theme/library') }}` - Load CSS/JS
|
||||
- `{{ url('route.name') }}` - Generate URLs
|
||||
- `{{ path('route.name') }}` - Generate paths
|
||||
- `{{ file_url('public://image.jpg') }}` - File URLs
|
||||
- `{{ content|without('field') }}` - Exclude fields
|
||||
|
||||
**Common templates:**
|
||||
- `page.html.twig` - Page layout structure
|
||||
- `node.html.twig` - Node display
|
||||
- `block.html.twig` - Block rendering
|
||||
- `field.html.twig` - Field output
|
||||
- `views-view.html.twig` - Views output
|
||||
|
||||
### 3. Preprocessing Functions
|
||||
|
||||
Modify template variables before rendering:
|
||||
|
||||
**Preprocess pattern:**
|
||||
```php
|
||||
function THEMENAME_preprocess_HOOK(&$variables) {
|
||||
// Add or modify variables
|
||||
// Access entities, services, configuration
|
||||
// Prepare data for templates
|
||||
}
|
||||
```
|
||||
|
||||
**Common preprocess hooks:**
|
||||
- `hook_preprocess_page()` - Page-level variables
|
||||
- `hook_preprocess_node()` - Node-specific data
|
||||
- `hook_preprocess_block()` - Block modifications
|
||||
- `hook_preprocess_field()` - Field alterations
|
||||
- `hook_preprocess_html()` - HTML document
|
||||
|
||||
**Best practices:**
|
||||
- Keep logic in preprocess, markup in templates
|
||||
- Use dependency injection when possible
|
||||
- Cache properly with cache contexts/tags
|
||||
- Document complex preprocessing
|
||||
|
||||
### 4. Template Suggestions
|
||||
|
||||
Provide specific templates for different contexts:
|
||||
|
||||
**Suggestion patterns:**
|
||||
```
|
||||
page--front.html.twig # Homepage
|
||||
page--node--{nid}.html.twig # Specific node
|
||||
page--node--{type}.html.twig # Content type
|
||||
node--{type}--{viewmode}.html.twig # Type + view mode
|
||||
block--{plugin-id}.html.twig # Specific block
|
||||
field--{entity}--{field}.html.twig # Specific field
|
||||
```
|
||||
|
||||
**Adding suggestions:**
|
||||
```php
|
||||
function THEMENAME_theme_suggestions_HOOK_alter(array &$suggestions, array $variables) {
|
||||
// Add custom suggestions
|
||||
$suggestions[] = 'custom__suggestion';
|
||||
}
|
||||
```
|
||||
|
||||
### 5. CSS & JavaScript Management
|
||||
|
||||
Organize and load theme assets efficiently:
|
||||
|
||||
**Library definition:**
|
||||
```yaml
|
||||
# THEMENAME.libraries.yml
|
||||
global-styling:
|
||||
version: 1.0
|
||||
css:
|
||||
base:
|
||||
css/base/reset.css: {}
|
||||
theme:
|
||||
css/theme/styles.css: {}
|
||||
js:
|
||||
js/custom.js: {}
|
||||
dependencies:
|
||||
- core/drupal
|
||||
```
|
||||
|
||||
**Loading libraries:**
|
||||
- Global: Define in `.info.yml`
|
||||
- Conditional: Use `attach_library()` in templates
|
||||
- Preprocessed: Attach in preprocess functions
|
||||
|
||||
**Best practices:**
|
||||
- Separate CSS into layers (base, layout, component, theme)
|
||||
- Use CSS aggregation in production
|
||||
- Minimize JavaScript dependencies
|
||||
- Leverage Drupal's asset library system
|
||||
|
||||
### 6. Responsive Design
|
||||
|
||||
Implement mobile-first, accessible designs:
|
||||
|
||||
**Breakpoints definition:**
|
||||
```yaml
|
||||
# THEMENAME.breakpoints.yml
|
||||
THEMENAME.mobile:
|
||||
label: Mobile
|
||||
mediaQuery: 'screen and (min-width: 0px)'
|
||||
weight: 0
|
||||
|
||||
THEMENAME.tablet:
|
||||
label: Tablet
|
||||
mediaQuery: 'screen and (min-width: 768px)'
|
||||
weight: 1
|
||||
|
||||
THEMENAME.desktop:
|
||||
label: Desktop
|
||||
mediaQuery: 'screen and (min-width: 1024px)'
|
||||
weight: 2
|
||||
```
|
||||
|
||||
**Mobile-first approach:**
|
||||
1. Design for smallest screens first
|
||||
2. Enhance for larger viewports
|
||||
3. Use responsive images
|
||||
4. Test across devices
|
||||
5. Follow accessibility standards (WCAG)
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### Creating a New Theme
|
||||
|
||||
1. **Scaffold the theme:**
|
||||
```bash
|
||||
cp -r assets/theme-template/ /path/to/drupal/themes/custom/mytheme/
|
||||
cd /path/to/drupal/themes/custom/mytheme/
|
||||
|
||||
# Rename files
|
||||
mv THEMENAME.info.yml mytheme.info.yml
|
||||
mv THEMENAME.libraries.yml mytheme.libraries.yml
|
||||
mv THEMENAME.theme mytheme.theme
|
||||
```
|
||||
|
||||
2. **Update theme configuration:**
|
||||
- Edit `mytheme.info.yml` - Set name, regions, base theme
|
||||
- Edit `mytheme.libraries.yml` - Define CSS/JS libraries
|
||||
- Replace `THEMENAME` with your machine name
|
||||
- Replace `THEMELABEL` with your label
|
||||
|
||||
3. **Enable the theme:**
|
||||
```bash
|
||||
ddev drush cr
|
||||
# Enable via UI at /admin/appearance or:
|
||||
ddev drush config:set system.theme default mytheme -y
|
||||
```
|
||||
|
||||
4. **Enable Twig debugging:**
|
||||
- Copy `sites/default/default.services.yml` to `sites/default/services.yml`
|
||||
- Set `twig.config.debug: true`
|
||||
- Set `twig.config.auto_reload: true`
|
||||
- Set `twig.config.cache: false`
|
||||
- Run `ddev drush cr`
|
||||
|
||||
5. **Develop and iterate:**
|
||||
- Modify templates in `templates/`
|
||||
- Update CSS in `css/`
|
||||
- Clear cache frequently: `ddev drush cr`
|
||||
- Check browser console for errors
|
||||
|
||||
### Standard Development Workflow
|
||||
|
||||
1. **Enable development settings** (Twig debug, disable CSS/JS aggregation)
|
||||
2. **Create/modify templates** based on suggestions from Twig debug
|
||||
3. **Implement preprocessing** in `.theme` file for complex data manipulation
|
||||
4. **Add CSS/JS** via libraries system
|
||||
5. **Test** across browsers and devices
|
||||
6. **Clear cache** after changes: `ddev drush cr`
|
||||
|
||||
### Finding Template Names
|
||||
|
||||
With Twig debugging enabled, inspect HTML source:
|
||||
|
||||
```html
|
||||
<!-- FILE NAME SUGGESTIONS:
|
||||
* page--node--123.html.twig
|
||||
* page--node--article.html.twig
|
||||
* page--node.html.twig
|
||||
x page.html.twig
|
||||
-->
|
||||
```
|
||||
|
||||
The `x` indicates the active template. Others are suggestions you can create.
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Adding Custom Variables in Preprocess
|
||||
|
||||
```php
|
||||
function mytheme_preprocess_page(&$variables) {
|
||||
// Add site slogan
|
||||
$variables['site_slogan'] = \Drupal::config('system.site')->get('slogan');
|
||||
|
||||
// Add current user
|
||||
$variables['is_logged_in'] = \Drupal::currentUser()->isAuthenticated();
|
||||
|
||||
// Add custom class
|
||||
$variables['attributes']['class'][] = 'custom-page-class';
|
||||
}
|
||||
```
|
||||
|
||||
### Template Inheritance
|
||||
|
||||
```twig
|
||||
{# templates/page--article.html.twig #}
|
||||
{% extends "page.html.twig" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="article-wrapper">
|
||||
{{ parent() }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
```
|
||||
|
||||
### Conditional Library Loading
|
||||
|
||||
```php
|
||||
function mytheme_preprocess_node(&$variables) {
|
||||
if ($variables['node']->bundle() == 'article') {
|
||||
$variables['#attached']['library'][] = 'mytheme/article-styles';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Accessing Field Values in Twig
|
||||
|
||||
```twig
|
||||
{# Get field value #}
|
||||
{{ node.field_custom.value }}
|
||||
|
||||
{# Check if field has value #}
|
||||
{% if node.field_image|render %}
|
||||
{{ content.field_image }}
|
||||
{% endif %}
|
||||
|
||||
{# Loop through multi-value field #}
|
||||
{% for item in node.field_tags %}
|
||||
{{ item.entity.label }}
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Theme Development
|
||||
1. **Base theme**: Choose appropriate base (Olivero, Claro, or none)
|
||||
2. **Structure**: Organize CSS logically (base, layout, components, theme)
|
||||
3. **Naming**: Use BEM or similar CSS methodology
|
||||
4. **Comments**: Document complex Twig logic and preprocessing
|
||||
5. **Performance**: Optimize images, minimize CSS/JS, lazy load when possible
|
||||
|
||||
### Twig Templates
|
||||
1. **Logic**: Keep complex logic in preprocess, not templates
|
||||
2. **Filters**: Use appropriate filters (|t, |clean_class, |safe_join)
|
||||
3. **Whitespace**: Use {% spaceless %} to control output
|
||||
4. **Debugging**: Enable Twig debugging during development only
|
||||
5. **Suggestions**: Use specific templates only when needed
|
||||
|
||||
### Preprocessing
|
||||
1. **Services**: Use dependency injection in theme service subscribers
|
||||
2. **Caching**: Add proper cache contexts and tags
|
||||
3. **Performance**: Avoid heavy operations in frequently-called preprocessors
|
||||
4. **Organization**: Group related preprocessing logically
|
||||
|
||||
### CSS & JavaScript
|
||||
1. **Libraries**: Group related assets into logical libraries
|
||||
2. **Dependencies**: Declare all library dependencies
|
||||
3. **Loading**: Load globally only when necessary
|
||||
4. **Aggregation**: Enable in production for performance
|
||||
|
||||
### Accessibility
|
||||
1. **Semantic HTML**: Use proper elements (header, nav, main, etc.)
|
||||
2. **ARIA**: Add labels and roles when needed
|
||||
3. **Keyboard**: Ensure keyboard navigation works
|
||||
4. **Contrast**: Meet WCAG color contrast requirements
|
||||
5. **Alt text**: Provide for all images
|
||||
|
||||
### Responsive Design
|
||||
1. **Mobile-first**: Design for small screens, enhance for large
|
||||
2. **Breakpoints**: Define logical breakpoints
|
||||
3. **Images**: Use responsive image styles
|
||||
4. **Testing**: Test across devices and screen sizes
|
||||
5. **Performance**: Optimize for mobile networks
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Template Changes Not Showing
|
||||
- Clear cache: `ddev drush cr`
|
||||
- Verify file location and naming
|
||||
- Check Twig debug for active template
|
||||
- Ensure library is properly defined
|
||||
|
||||
### CSS/JS Not Loading
|
||||
- Check library definition in `.libraries.yml`
|
||||
- Verify file paths are correct
|
||||
- Ensure library is attached (globally or conditionally)
|
||||
- Clear cache and check browser console
|
||||
|
||||
### Variables Not Available in Template
|
||||
- Check preprocess function naming
|
||||
- Verify variables are being set correctly
|
||||
- Use Twig debugging: `{{ dump() }}`
|
||||
- Clear cache after preprocessing changes
|
||||
|
||||
### Twig Debugging Not Working
|
||||
- Verify `sites/default/services.yml` exists
|
||||
- Check `twig.config.debug: true` is set
|
||||
- Clear cache: `ddev drush cr`
|
||||
- Check file permissions
|
||||
|
||||
## Resources
|
||||
|
||||
### Reference Documentation
|
||||
|
||||
- **`references/theming.md`** - Comprehensive theming guide
|
||||
- Twig syntax and filters
|
||||
- Template suggestions
|
||||
- Preprocessing patterns
|
||||
- Library management
|
||||
- Responsive design
|
||||
- Best practices
|
||||
|
||||
### Asset Templates
|
||||
|
||||
- **`assets/theme-template/`** - Complete theme scaffold
|
||||
- `.info.yml` with regions and configuration
|
||||
- `.libraries.yml` with CSS/JS examples
|
||||
- `.theme` with preprocess examples
|
||||
- Page template
|
||||
- CSS and JS starter files
|
||||
|
||||
### Searching References
|
||||
|
||||
```bash
|
||||
# Find specific Twig filter
|
||||
grep -r "clean_class" references/
|
||||
|
||||
# Find preprocessing examples
|
||||
grep -r "preprocess_node" references/
|
||||
|
||||
# Find library patterns
|
||||
grep -r "libraries.yml" references/
|
||||
```
|
||||
|
||||
## Version Compatibility
|
||||
|
||||
### Drupal 8 vs 9 vs 10 vs 11
|
||||
- **Twig syntax**: Consistent across all versions
|
||||
- **Base themes**: Some legacy themes removed in 10+
|
||||
- **Libraries**: Same structure across versions
|
||||
- **jQuery**: Drupal 9+ doesn't load jQuery by default
|
||||
- **Claro**: Admin theme standard in 9+
|
||||
- **Olivero**: Frontend theme standard in 10+
|
||||
|
||||
### Migration Notes
|
||||
- When upgrading, check for deprecated base themes
|
||||
- Review jQuery dependencies
|
||||
- Test with current Twig version
|
||||
- Check for removed core templates
|
||||
|
||||
## See Also
|
||||
|
||||
- **drupal-backend** - Module development, hooks, APIs
|
||||
- **drupal-tooling** - DDEV and Drush development tools
|
||||
- [Drupal Theming Guide](https://www.drupal.org/docs/theming-drupal) - Official documentation
|
||||
- [Twig Documentation](https://twig.symfony.com/doc/) - Twig syntax reference
|
||||
63
assets/theme-template/README.md
Normal file
63
assets/theme-template/README.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# Theme Template
|
||||
|
||||
This is a basic Drupal theme template. To use it:
|
||||
|
||||
1. Copy this directory to your Drupal installation: `themes/custom/yourthemename/`
|
||||
2. Rename files from `THEMENAME.*` to `yourthemename.*`
|
||||
3. Replace all instances of `THEMENAME` with your theme's machine name (lowercase, underscores)
|
||||
4. Replace all instances of `THEMELABEL` with your theme's human-readable name
|
||||
5. Customize the theme as needed
|
||||
|
||||
## Files Included
|
||||
|
||||
- `THEMENAME.info.yml` - Theme metadata
|
||||
- `THEMENAME.libraries.yml` - CSS/JS library definitions
|
||||
- `THEMENAME.theme` - Theme functions and preprocessing
|
||||
- `templates/page.html.twig` - Page template
|
||||
- `css/base/reset.css` - Base CSS reset
|
||||
- `js/custom.js` - Custom JavaScript
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
THEMENAME/
|
||||
├── THEMENAME.info.yml
|
||||
├── THEMENAME.libraries.yml
|
||||
├── THEMENAME.theme
|
||||
├── css/
|
||||
│ └── base/
|
||||
│ └── reset.css
|
||||
├── js/
|
||||
│ └── custom.js
|
||||
└── templates/
|
||||
└── page.html.twig
|
||||
```
|
||||
|
||||
You can expand the CSS structure as needed:
|
||||
```
|
||||
css/
|
||||
├── base/
|
||||
│ └── reset.css
|
||||
├── layout/
|
||||
│ └── layout.css
|
||||
├── components/
|
||||
│ └── components.css
|
||||
└── theme/
|
||||
└── theme.css
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
After creating your theme:
|
||||
|
||||
1. Clear cache: `ddev drush cr`
|
||||
2. Enable your theme: Go to `/admin/appearance`
|
||||
3. Set as default theme
|
||||
4. Start customizing!
|
||||
|
||||
## Development Tips
|
||||
|
||||
- Enable Twig debugging in `sites/default/services.yml`
|
||||
- Disable CSS/JS aggregation during development
|
||||
- Clear cache frequently: `ddev drush cr`
|
||||
- Use template suggestions for specific pages/content types
|
||||
40
assets/theme-template/THEMENAME.info.yml
Normal file
40
assets/theme-template/THEMENAME.info.yml
Normal file
@@ -0,0 +1,40 @@
|
||||
name: THEMELABEL
|
||||
description: 'A custom Drupal theme.'
|
||||
type: theme
|
||||
core_version_requirement: ^9 || ^10 || ^11
|
||||
package: Custom
|
||||
|
||||
# Base theme (uncomment one)
|
||||
# base theme: stable9
|
||||
# base theme: claro
|
||||
# base theme: olivero
|
||||
base theme: false
|
||||
|
||||
# Regions
|
||||
regions:
|
||||
header: Header
|
||||
primary_menu: 'Primary menu'
|
||||
secondary_menu: 'Secondary menu'
|
||||
page_top: 'Page top'
|
||||
page_bottom: 'Page bottom'
|
||||
highlighted: Highlighted
|
||||
breadcrumb: Breadcrumb
|
||||
content: Content
|
||||
sidebar_first: 'Sidebar first'
|
||||
sidebar_second: 'Sidebar second'
|
||||
footer: Footer
|
||||
|
||||
# Libraries
|
||||
libraries:
|
||||
- THEMENAME/global-styling
|
||||
- THEMENAME/global-scripts
|
||||
|
||||
# Component libraries (optional)
|
||||
# component-libraries:
|
||||
# atoms:
|
||||
# paths:
|
||||
# - components/atoms
|
||||
|
||||
# Logo and favicon
|
||||
# logo: images/logo.svg
|
||||
# favicon: images/favicon.ico
|
||||
19
assets/theme-template/THEMENAME.libraries.yml
Normal file
19
assets/theme-template/THEMENAME.libraries.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
global-styling:
|
||||
version: 1.0
|
||||
css:
|
||||
base:
|
||||
css/base/reset.css: {}
|
||||
layout:
|
||||
css/layout/layout.css: {}
|
||||
component:
|
||||
css/components/components.css: {}
|
||||
theme:
|
||||
css/theme/theme.css: {}
|
||||
|
||||
global-scripts:
|
||||
version: 1.0
|
||||
js:
|
||||
js/custom.js: {}
|
||||
dependencies:
|
||||
- core/drupal
|
||||
- core/drupalSettings
|
||||
39
assets/theme-template/THEMENAME.theme
Normal file
39
assets/theme-template/THEMENAME.theme
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Functions to support theming in the THEMELABEL theme.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Implements hook_preprocess_HOOK() for page templates.
|
||||
*/
|
||||
function THEMENAME_preprocess_page(&$variables) {
|
||||
// Add custom variables or modify existing ones.
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_preprocess_HOOK() for node templates.
|
||||
*/
|
||||
function THEMENAME_preprocess_node(&$variables) {
|
||||
/** @var \Drupal\node\NodeInterface $node */
|
||||
$node = $variables['node'];
|
||||
|
||||
// Add custom variables based on node.
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_theme_suggestions_HOOK_alter() for page templates.
|
||||
*/
|
||||
function THEMENAME_theme_suggestions_page_alter(array &$suggestions, array $variables) {
|
||||
// Add custom template suggestions.
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_alter().
|
||||
*/
|
||||
function THEMENAME_form_alter(&$form, FormStateInterface $form_state, $form_id) {
|
||||
// Modify forms as needed.
|
||||
}
|
||||
26
assets/theme-template/css/base/reset.css
Normal file
26
assets/theme-template/css/base/reset.css
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* @file
|
||||
* Basic CSS reset and normalization.
|
||||
*/
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
color: #333;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
16
assets/theme-template/js/custom.js
Normal file
16
assets/theme-template/js/custom.js
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @file
|
||||
* Custom JavaScript for THEMELABEL theme.
|
||||
*/
|
||||
|
||||
(function (Drupal) {
|
||||
'use strict';
|
||||
|
||||
Drupal.behaviors.THEMENAME = {
|
||||
attach: function (context, settings) {
|
||||
// Custom JavaScript code here
|
||||
console.log('THEMELABEL theme loaded');
|
||||
}
|
||||
};
|
||||
|
||||
})(Drupal);
|
||||
57
assets/theme-template/templates/page.html.twig
Normal file
57
assets/theme-template/templates/page.html.twig
Normal file
@@ -0,0 +1,57 @@
|
||||
{#
|
||||
/**
|
||||
* @file
|
||||
* Theme override to display a page.
|
||||
*/
|
||||
#}
|
||||
<div class="layout-container">
|
||||
{% if page.header %}
|
||||
<header role="banner" class="site-header">
|
||||
{{ page.header }}
|
||||
</header>
|
||||
{% endif %}
|
||||
|
||||
{% if page.primary_menu %}
|
||||
<nav role="navigation" class="primary-menu">
|
||||
{{ page.primary_menu }}
|
||||
</nav>
|
||||
{% endif %}
|
||||
|
||||
{% if page.breadcrumb %}
|
||||
<div class="breadcrumb-wrapper">
|
||||
{{ page.breadcrumb }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if page.highlighted %}
|
||||
<div class="highlighted">
|
||||
{{ page.highlighted }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<main role="main" class="main-content">
|
||||
<a id="main-content" tabindex="-1"></a>
|
||||
|
||||
<div class="layout-content">
|
||||
{{ page.content }}
|
||||
</div>
|
||||
|
||||
{% if page.sidebar_first %}
|
||||
<aside class="sidebar sidebar--first" role="complementary">
|
||||
{{ page.sidebar_first }}
|
||||
</aside>
|
||||
{% endif %}
|
||||
|
||||
{% if page.sidebar_second %}
|
||||
<aside class="sidebar sidebar--second" role="complementary">
|
||||
{{ page.sidebar_second }}
|
||||
</aside>
|
||||
{% endif %}
|
||||
</main>
|
||||
|
||||
{% if page.footer %}
|
||||
<footer role="contentinfo" class="site-footer">
|
||||
{{ page.footer }}
|
||||
</footer>
|
||||
{% endif %}
|
||||
</div>
|
||||
77
plugin.lock.json
Normal file
77
plugin.lock.json
Normal file
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"$schema": "internal://schemas/plugin.lock.v1.json",
|
||||
"pluginId": "gh:Omedia/drupal-skill:drupal-frontend",
|
||||
"normalized": {
|
||||
"repo": null,
|
||||
"ref": "refs/tags/v20251128.0",
|
||||
"commit": "41cbbcfc5b908d2a4c909c8ed3cf4bf346d8347d",
|
||||
"treeHash": "246b3d55adff0aa493e95588c8d801d78afdbb17202b57bd7c53e8765536c031",
|
||||
"generatedAt": "2025-11-28T10:12:18.414950Z",
|
||||
"toolVersion": "publish_plugins.py@0.2.0"
|
||||
},
|
||||
"origin": {
|
||||
"remote": "git@github.com:zhongweili/42plugin-data.git",
|
||||
"branch": "master",
|
||||
"commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390",
|
||||
"repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data"
|
||||
},
|
||||
"manifest": {
|
||||
"name": "drupal-frontend",
|
||||
"description": "Drupal Front End Specialist skill for theme development, Twig templates, and rendering system (Drupal 8-11+). Use when working with Drupal themes, Twig syntax, preprocessing, CSS/JS libraries, or template suggestions.",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"content": {
|
||||
"files": [
|
||||
{
|
||||
"path": "README.md",
|
||||
"sha256": "5e23c9576a6751990607cd10129f80a3a93456d7edefa59cd3e467d3337e9312"
|
||||
},
|
||||
{
|
||||
"path": "SKILL.md",
|
||||
"sha256": "ff8d70723699a19b27030618b278f28ce8480489866e02de73ec889e5afd3791"
|
||||
},
|
||||
{
|
||||
"path": "references/theming.md",
|
||||
"sha256": "2dc93f7f888dcf8f108dc034a59c5290dfb03b60d54fefc5e5224bc09767dd09"
|
||||
},
|
||||
{
|
||||
"path": ".claude-plugin/plugin.json",
|
||||
"sha256": "a50e29c08f31276d4a3070447cbb2d42f17d6e90c15b86f5ec138e60832a8a5b"
|
||||
},
|
||||
{
|
||||
"path": "assets/theme-template/THEMENAME.libraries.yml",
|
||||
"sha256": "e7348c8ee5729c458f1189aa4559398e5b92568b4b790c869eda020af5ca5bed"
|
||||
},
|
||||
{
|
||||
"path": "assets/theme-template/README.md",
|
||||
"sha256": "6e45999798046e8bcfa6ce4d98a3a46f5902b7083af37319549403ed98f62825"
|
||||
},
|
||||
{
|
||||
"path": "assets/theme-template/THEMENAME.info.yml",
|
||||
"sha256": "25a0760f6de46d0eed90d4b5a183404248b70a5f297bf28b4a92c6617c7465bd"
|
||||
},
|
||||
{
|
||||
"path": "assets/theme-template/THEMENAME.theme",
|
||||
"sha256": "9219803ea44915d93d50f4c5ae813eb1c29cc35b7bcaa0a3e1514bb18435ba22"
|
||||
},
|
||||
{
|
||||
"path": "assets/theme-template/css/base/reset.css",
|
||||
"sha256": "3a548377de02f63a2b44019f206200b368cfca5af624b6413aed879ac6bad628"
|
||||
},
|
||||
{
|
||||
"path": "assets/theme-template/js/custom.js",
|
||||
"sha256": "edef32819f6f1f7682063f228ccd11f9ccaf920e602998806bad1f7541341010"
|
||||
},
|
||||
{
|
||||
"path": "assets/theme-template/templates/page.html.twig",
|
||||
"sha256": "0bd1013051a32a702fe2fe8c494be3cb81721918ebff535fdc1eb1265943fec6"
|
||||
}
|
||||
],
|
||||
"dirSha256": "246b3d55adff0aa493e95588c8d801d78afdbb17202b57bd7c53e8765536c031"
|
||||
},
|
||||
"security": {
|
||||
"scannedAt": null,
|
||||
"scannerVersion": null,
|
||||
"flags": []
|
||||
}
|
||||
}
|
||||
563
references/theming.md
Normal file
563
references/theming.md
Normal file
@@ -0,0 +1,563 @@
|
||||
# Drupal Theming Reference
|
||||
|
||||
Comprehensive guide to Drupal theming for Drupal 8-11+.
|
||||
|
||||
## Theme Structure
|
||||
|
||||
```
|
||||
mytheme/
|
||||
├── mytheme.info.yml # Theme metadata (required)
|
||||
├── mytheme.libraries.yml # CSS/JS libraries
|
||||
├── mytheme.theme # Theme functions and preprocess
|
||||
├── mytheme.breakpoints.yml # Responsive breakpoints
|
||||
├── logo.svg # Theme logo
|
||||
├── screenshot.png # Admin screenshot
|
||||
├── composer.json # Composer dependencies
|
||||
├── package.json # NPM dependencies (if using build tools)
|
||||
├── config/
|
||||
│ ├── install/ # Default configuration
|
||||
│ └── schema/ # Configuration schema
|
||||
├── css/
|
||||
│ ├── base/
|
||||
│ ├── components/
|
||||
│ ├── layout/
|
||||
│ └── theme/
|
||||
├── js/
|
||||
│ └── custom.js
|
||||
├── images/
|
||||
├── templates/
|
||||
│ ├── block/
|
||||
│ ├── content/
|
||||
│ ├── field/
|
||||
│ ├── layout/
|
||||
│ ├── navigation/
|
||||
│ ├── views/
|
||||
│ ├── page.html.twig
|
||||
│ ├── node.html.twig
|
||||
│ └── block.html.twig
|
||||
└── src/ # Optional PHP classes
|
||||
└── Plugin/
|
||||
└── Preprocess/
|
||||
```
|
||||
|
||||
## Theme Info File (mytheme.info.yml)
|
||||
|
||||
```yaml
|
||||
name: My Theme
|
||||
type: theme
|
||||
description: 'A custom Drupal theme'
|
||||
core_version_requirement: ^9 || ^10 || ^11
|
||||
package: Custom
|
||||
|
||||
# Base theme
|
||||
base theme: stable9
|
||||
# Or for no base theme:
|
||||
# base theme: false
|
||||
|
||||
# Regions
|
||||
regions:
|
||||
header: Header
|
||||
primary_menu: 'Primary menu'
|
||||
secondary_menu: 'Secondary menu'
|
||||
page_top: 'Page top'
|
||||
page_bottom: 'Page bottom'
|
||||
highlighted: Highlighted
|
||||
breadcrumb: Breadcrumb
|
||||
content: Content
|
||||
sidebar_first: 'Sidebar first'
|
||||
sidebar_second: 'Sidebar second'
|
||||
footer: Footer
|
||||
|
||||
# Libraries to load on all pages
|
||||
libraries:
|
||||
- mytheme/global-styling
|
||||
- mytheme/global-scripts
|
||||
|
||||
# Libraries to load only when certain conditions are met
|
||||
libraries-override:
|
||||
# Replace core library
|
||||
core/drupal.dialog:
|
||||
mytheme/custom-dialog: {}
|
||||
|
||||
libraries-extend:
|
||||
# Add to existing library
|
||||
core/drupal.dialog:
|
||||
- mytheme/dialog-extend
|
||||
|
||||
# Remove libraries
|
||||
libraries-override:
|
||||
core/normalize:
|
||||
css:
|
||||
base:
|
||||
assets/vendor/normalize-css/normalize.css: false
|
||||
|
||||
# Component libraries (for single-directory components)
|
||||
component-libraries:
|
||||
atoms:
|
||||
paths:
|
||||
- components/atoms
|
||||
molecules:
|
||||
paths:
|
||||
- components/molecules
|
||||
|
||||
# Logo and favicon
|
||||
logo: images/logo.svg
|
||||
favicon: images/favicon.ico
|
||||
|
||||
# Stylesheets to remove
|
||||
stylesheets-remove:
|
||||
- core/assets/vendor/normalize-css/normalize.css
|
||||
- '@classy/css/components/tabs.css'
|
||||
|
||||
# CKEditor stylesheet
|
||||
ckeditor_stylesheets:
|
||||
- css/ckeditor.css
|
||||
|
||||
# Hidden theme (for base themes)
|
||||
hidden: false
|
||||
```
|
||||
|
||||
## Libraries (mytheme.libraries.yml)
|
||||
|
||||
```yaml
|
||||
global-styling:
|
||||
version: 1.0
|
||||
css:
|
||||
base:
|
||||
css/base/reset.css: {}
|
||||
layout:
|
||||
css/layout/layout.css: {}
|
||||
component:
|
||||
css/components/button.css: {}
|
||||
css/components/card.css: {}
|
||||
theme:
|
||||
css/theme/colors.css: {}
|
||||
css/theme/typography.css: {}
|
||||
|
||||
global-scripts:
|
||||
version: 1.0
|
||||
js:
|
||||
js/custom.js: {}
|
||||
dependencies:
|
||||
- core/drupal
|
||||
- core/jquery
|
||||
|
||||
# Conditional library (loaded via preprocess or template)
|
||||
modal:
|
||||
version: 1.0
|
||||
css:
|
||||
component:
|
||||
css/components/modal.css: {}
|
||||
js:
|
||||
js/modal.js: {}
|
||||
dependencies:
|
||||
- core/drupal
|
||||
- core/drupalSettings
|
||||
|
||||
# External library
|
||||
fontawesome:
|
||||
version: 6.0
|
||||
css:
|
||||
theme:
|
||||
https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css: { type: external, minified: true }
|
||||
|
||||
# SCSS/SASS (requires compilation)
|
||||
compiled-styles:
|
||||
version: 1.0
|
||||
css:
|
||||
theme:
|
||||
dist/css/styles.css: {}
|
||||
dependencies:
|
||||
- core/normalize
|
||||
```
|
||||
|
||||
## Theme Functions (mytheme.theme)
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Functions to support theming in the My Theme theme.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\node\NodeInterface;
|
||||
|
||||
/**
|
||||
* Implements hook_preprocess_HOOK() for page templates.
|
||||
*/
|
||||
function mytheme_preprocess_page(&$variables) {
|
||||
// Add custom variable to all pages
|
||||
$variables['site_slogan'] = \Drupal::config('system.site')->get('slogan');
|
||||
|
||||
// Add body classes
|
||||
$variables['attributes']['class'][] = 'custom-page-class';
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_preprocess_HOOK() for node templates.
|
||||
*/
|
||||
function mytheme_preprocess_node(&$variables) {
|
||||
/** @var \Drupal\node\NodeInterface $node */
|
||||
$node = $variables['node'];
|
||||
|
||||
// Add custom variables
|
||||
$variables['created_date'] = \Drupal::service('date.formatter')->format(
|
||||
$node->getCreatedTime(),
|
||||
'custom',
|
||||
'F j, Y'
|
||||
);
|
||||
|
||||
// Add template suggestions
|
||||
$variables['theme_hook_suggestions'][] = 'node__' . $node->bundle() . '__' . $variables['view_mode'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_preprocess_HOOK() for block templates.
|
||||
*/
|
||||
function mytheme_preprocess_block(&$variables) {
|
||||
// Add block ID as class
|
||||
if (isset($variables['elements']['#id'])) {
|
||||
$variables['attributes']['class'][] = 'block-' . $variables['elements']['#id'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_preprocess_HOOK() for field templates.
|
||||
*/
|
||||
function mytheme_preprocess_field(&$variables) {
|
||||
$element = $variables['element'];
|
||||
|
||||
// Custom preprocessing for specific fields
|
||||
if ($element['#field_name'] == 'field_custom') {
|
||||
$variables['custom_class'] = 'field-custom-class';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_theme_suggestions_HOOK_alter() for page templates.
|
||||
*/
|
||||
function mytheme_theme_suggestions_page_alter(array &$suggestions, array $variables) {
|
||||
// Add template suggestions based on current route
|
||||
if ($node = \Drupal::routeMatch()->getParameter('node')) {
|
||||
if ($node instanceof NodeInterface) {
|
||||
$suggestions[] = 'page__node__' . $node->bundle();
|
||||
$suggestions[] = 'page__node__' . $node->id();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_theme_suggestions_HOOK_alter() for node templates.
|
||||
*/
|
||||
function mytheme_theme_suggestions_node_alter(array &$suggestions, array $variables) {
|
||||
/** @var \Drupal\node\NodeInterface $node */
|
||||
$node = $variables['elements']['#node'];
|
||||
$view_mode = $variables['elements']['#view_mode'];
|
||||
|
||||
// Add suggestion for bundle and view mode combination
|
||||
$suggestions[] = 'node__' . $node->bundle() . '__' . $view_mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_alter().
|
||||
*/
|
||||
function mytheme_form_alter(&$form, FormStateInterface $form_state, $form_id) {
|
||||
// Add custom classes to forms
|
||||
if ($form_id == 'search_block_form') {
|
||||
$form['actions']['submit']['#attributes']['class'][] = 'custom-search-submit';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_page_attachments_alter().
|
||||
*/
|
||||
function mytheme_page_attachments_alter(array &$attachments) {
|
||||
// Add custom meta tags
|
||||
$meta_charset = [
|
||||
'#tag' => 'meta',
|
||||
'#attributes' => [
|
||||
'charset' => 'utf-8',
|
||||
],
|
||||
];
|
||||
$attachments['#attached']['html_head'][] = [$meta_charset, 'meta_charset'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_library_info_alter().
|
||||
*/
|
||||
function mytheme_library_info_alter(&$libraries, $extension) {
|
||||
// Modify libraries from other modules
|
||||
if ($extension == 'core' && isset($libraries['drupal.dialog'])) {
|
||||
$libraries['drupal.dialog']['dependencies'][] = 'mytheme/custom-dialog';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Twig Templates
|
||||
|
||||
### page.html.twig
|
||||
```twig
|
||||
<div class="layout-container">
|
||||
<header role="banner">
|
||||
{{ page.header }}
|
||||
{{ page.primary_menu }}
|
||||
</header>
|
||||
|
||||
{{ page.breadcrumb }}
|
||||
{{ page.highlighted }}
|
||||
|
||||
<main role="main" class="main-content">
|
||||
<a id="main-content" tabindex="-1"></a>
|
||||
|
||||
<div class="layout-content">
|
||||
{{ page.content }}
|
||||
</div>
|
||||
|
||||
{% if page.sidebar_first %}
|
||||
<aside class="sidebar sidebar--first" role="complementary">
|
||||
{{ page.sidebar_first }}
|
||||
</aside>
|
||||
{% endif %}
|
||||
|
||||
{% if page.sidebar_second %}
|
||||
<aside class="sidebar sidebar--second" role="complementary">
|
||||
{{ page.sidebar_second }}
|
||||
</aside>
|
||||
{% endif %}
|
||||
</main>
|
||||
|
||||
{% if page.footer %}
|
||||
<footer role="contentinfo">
|
||||
{{ page.footer }}
|
||||
</footer>
|
||||
{% endif %}
|
||||
</div>
|
||||
```
|
||||
|
||||
### node.html.twig
|
||||
```twig
|
||||
<article{{ attributes.addClass('node', 'node--type-' ~ node.bundle|clean_class, node.isPromoted() ? 'node--promoted', node.isSticky() ? 'node--sticky', not node.isPublished() ? 'node--unpublished', view_mode ? 'node--view-mode-' ~ view_mode|clean_class) }}>
|
||||
|
||||
{{ title_prefix }}
|
||||
{% if not page %}
|
||||
<h2{{ title_attributes.addClass('node__title') }}>
|
||||
<a href="{{ url }}" rel="bookmark">{{ label }}</a>
|
||||
</h2>
|
||||
{% endif %}
|
||||
{{ title_suffix }}
|
||||
|
||||
{% if display_submitted %}
|
||||
<div class="node__meta">
|
||||
{{ author_picture }}
|
||||
<span{{ author_attributes }}>
|
||||
{% trans %}Submitted by {{ author_name }} on {{ date }}{% endtrans %}
|
||||
</span>
|
||||
{{ metadata }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div{{ content_attributes.addClass('node__content') }}>
|
||||
{{ content }}
|
||||
</div>
|
||||
|
||||
</article>
|
||||
```
|
||||
|
||||
### block.html.twig
|
||||
```twig
|
||||
<div{{ attributes.addClass('block', 'block-' ~ configuration.provider|clean_class, 'block-' ~ plugin_id|clean_class) }}>
|
||||
{{ title_prefix }}
|
||||
{% if label %}
|
||||
<h2{{ title_attributes }}>{{ label }}</h2>
|
||||
{% endif %}
|
||||
{{ title_suffix }}
|
||||
|
||||
{% block content %}
|
||||
{{ content }}
|
||||
{% endblock %}
|
||||
</div>
|
||||
```
|
||||
|
||||
### field.html.twig
|
||||
```twig
|
||||
{% if multiple %}
|
||||
<div{{ attributes.addClass('field', 'field--name-' ~ field_name|clean_class, 'field--type-' ~ field_type|clean_class) }}>
|
||||
{% if label %}
|
||||
<div{{ title_attributes.addClass('field__label') }}>{{ label }}</div>
|
||||
{% endif %}
|
||||
<div class="field__items">
|
||||
{% for item in items %}
|
||||
<div{{ item.attributes.addClass('field__item') }}>{{ item.content }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
{% for item in items %}
|
||||
<div{{ attributes.addClass('field', 'field--name-' ~ field_name|clean_class, 'field--type-' ~ field_type|clean_class) }}>
|
||||
{% if label %}
|
||||
<div{{ title_attributes.addClass('field__label') }}>{{ label }}</div>
|
||||
{% endif %}
|
||||
<div{{ item.attributes.addClass('field__item') }}>{{ item.content }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
## Twig Filters & Functions
|
||||
|
||||
### Common Filters
|
||||
```twig
|
||||
{# Translate #}
|
||||
{{ 'Hello World'|t }}
|
||||
|
||||
{# Clean class name #}
|
||||
{{ 'Field Name'|clean_class }}
|
||||
|
||||
{# Format date #}
|
||||
{{ node.created.value|date('F j, Y') }}
|
||||
|
||||
{# Safe join #}
|
||||
{{ items|safe_join(', ') }}
|
||||
|
||||
{# Render #}
|
||||
{{ content|render }}
|
||||
|
||||
{# Without (remove array elements) #}
|
||||
{{ content|without('field_image') }}
|
||||
|
||||
{# Placeholder #}
|
||||
{{ 'Hello @name'|t({'@name': name}) }}
|
||||
```
|
||||
|
||||
### Common Functions
|
||||
```twig
|
||||
{# Attach library #}
|
||||
{{ attach_library('mytheme/modal') }}
|
||||
|
||||
{# URL #}
|
||||
<a href="{{ url('entity.node.canonical', {'node': node.id}) }}">Link</a>
|
||||
|
||||
{# Path #}
|
||||
<a href="{{ path('entity.node.canonical', {'node': node.id}) }}">Link</a>
|
||||
|
||||
{# File URL #}
|
||||
{{ file_url('public://image.jpg') }}
|
||||
|
||||
{# Create attribute #}
|
||||
{% set attributes = create_attribute() %}
|
||||
{{ attributes.addClass('custom-class') }}
|
||||
```
|
||||
|
||||
## Breakpoints (mytheme.breakpoints.yml)
|
||||
|
||||
```yaml
|
||||
mytheme.mobile:
|
||||
label: Mobile
|
||||
mediaQuery: 'screen and (min-width: 0px)'
|
||||
weight: 0
|
||||
multipliers:
|
||||
- 1x
|
||||
- 2x
|
||||
|
||||
mytheme.tablet:
|
||||
label: Tablet
|
||||
mediaQuery: 'screen and (min-width: 768px)'
|
||||
weight: 1
|
||||
multipliers:
|
||||
- 1x
|
||||
- 2x
|
||||
|
||||
mytheme.desktop:
|
||||
label: Desktop
|
||||
mediaQuery: 'screen and (min-width: 1024px)'
|
||||
weight: 2
|
||||
multipliers:
|
||||
- 1x
|
||||
- 2x
|
||||
|
||||
mytheme.wide:
|
||||
label: Wide
|
||||
mediaQuery: 'screen and (min-width: 1440px)'
|
||||
weight: 3
|
||||
multipliers:
|
||||
- 1x
|
||||
```
|
||||
|
||||
## Debug Mode
|
||||
|
||||
Enable Twig debugging in development:
|
||||
|
||||
**sites/default/services.yml**
|
||||
```yaml
|
||||
parameters:
|
||||
twig.config:
|
||||
debug: true
|
||||
auto_reload: true
|
||||
cache: false
|
||||
```
|
||||
|
||||
Then clear cache:
|
||||
```bash
|
||||
ddev drush cr
|
||||
```
|
||||
|
||||
## Theme Development Workflow
|
||||
|
||||
1. **Enable development settings**
|
||||
```bash
|
||||
# Copy and enable development settings
|
||||
cp sites/example.settings.local.php sites/default/settings.local.php
|
||||
```
|
||||
|
||||
2. **Disable CSS/JS aggregation**
|
||||
- Go to `/admin/config/development/performance`
|
||||
- Uncheck "Aggregate CSS files"
|
||||
- Uncheck "Aggregate JavaScript files"
|
||||
|
||||
3. **Clear cache frequently**
|
||||
```bash
|
||||
ddev drush cr
|
||||
```
|
||||
|
||||
4. **Use Twig debugging**
|
||||
- Check HTML source for template suggestions
|
||||
- Look for `<!-- BEGIN OUTPUT -->` comments
|
||||
|
||||
5. **Rebuild theme registry**
|
||||
```bash
|
||||
ddev drush drush cr
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **BEM Methodology**: Use BEM for CSS class naming
|
||||
2. **Component-based**: Build reusable components
|
||||
3. **Accessibility**: Follow WCAG guidelines
|
||||
4. **Performance**: Optimize images, minimize CSS/JS
|
||||
5. **Mobile-first**: Design for mobile, enhance for desktop
|
||||
6. **Semantic HTML**: Use proper HTML5 elements
|
||||
7. **Template suggestions**: Use specific templates when needed
|
||||
8. **Libraries**: Group related CSS/JS in libraries
|
||||
9. **Preprocessing**: Keep logic in .theme file, not templates
|
||||
10. **Documentation**: Comment complex template logic
|
||||
|
||||
## Common Template Suggestions
|
||||
|
||||
```
|
||||
page--front.html.twig # Front page
|
||||
page--node--123.html.twig # Specific node
|
||||
page--node--article.html.twig # Content type
|
||||
node--article--full.html.twig # Bundle and view mode
|
||||
node--123.html.twig # Specific node
|
||||
block--system-branding-block.html.twig # Specific block
|
||||
field--node--title--article.html.twig # Specific field
|
||||
views-view--blog--page-1.html.twig # View and display
|
||||
```
|
||||
|
||||
## Useful Resources
|
||||
|
||||
- Theming Guide: https://www.drupal.org/docs/theming-drupal
|
||||
- Twig Documentation: https://twig.symfony.com/doc/
|
||||
- Template Suggestions: https://www.drupal.org/docs/theming-drupal/twig-in-drupal/debugging-twig-templates
|
||||
Reference in New Issue
Block a user