hook_name_widget_layouts¶
Overview¶
The hook_name_widget_layouts() hook allows modules to provide custom layout options for the name field widget. Layouts control how name components are visually arranged in forms.
Hook Definition¶
/**
* Provide widget layout options.
*
* @return array
* A keyed array of layout settings.
* - label: The layout label (required).
* - library: An array of libraries to attach to the element.
* - wrapper_attributes: An array of wrapper attributes.
*/
function hook_name_widget_layouts() {
return [
'inline' => [
'label' => t('Inline'),
'library' => [
'name/widget.inline',
],
'wrapper_attributes' => [
'class' => ['form--inline', 'clearfix'],
],
],
];
}
Return Structure¶
The hook should return an associative array where:
- Key: Machine name of the layout
- Value: Array with the following keys:
label(required) - Translated label for the layoutlibrary(optional) - Array of library names to attachwrapper_attributes(optional) - Array of HTML attributes for the wrapper element
Layout Properties¶
label¶
The human-readable name displayed in the widget settings.
library¶
Drupal libraries to attach to the form element. Used for custom CSS/JavaScript.
Library must be defined in my_module.libraries.yml:
name-widget-layout:
version: 1.0
css:
theme:
css/name-widget-layout.css: {}
js:
js/name-widget-layout.js: {}
wrapper_attributes¶
HTML attributes applied to the wrapper container.
'wrapper_attributes' => [
'class' => ['form--inline', 'clearfix', 'name-widget-inline'],
'data-layout' => 'inline',
],
Complete Example¶
Custom Module Implementation¶
/**
* Implements hook_name_widget_layouts().
*/
function my_module_name_widget_layouts() {
return [
// Inline layout - all fields in one row
'inline' => [
'label' => t('Inline'),
'library' => ['my_module/name-inline'],
'wrapper_attributes' => [
'class' => ['name-widget-inline', 'clearfix'],
],
],
// Two-column layout
'two_column' => [
'label' => t('Two Columns'),
'library' => ['my_module/name-two-column'],
'wrapper_attributes' => [
'class' => ['name-widget-two-column', 'layout-columns-2'],
],
],
// Compact layout for mobile
'compact' => [
'label' => t('Compact (Mobile Friendly)'),
'library' => ['my_module/name-compact'],
'wrapper_attributes' => [
'class' => ['name-widget-compact'],
'data-responsive' => 'true',
],
],
// Grid layout
'grid' => [
'label' => t('Grid Layout'),
'library' => ['my_module/name-grid'],
'wrapper_attributes' => [
'class' => ['name-widget-grid', 'layout-grid'],
],
],
];
}
Library Definition (my_module.libraries.yml)¶
name-inline:
version: 1.0
css:
theme:
css/name-inline.css: {}
dependencies:
- core/drupal
name-two-column:
version: 1.0
css:
theme:
css/name-two-column.css: {}
dependencies:
- core/drupal
name-compact:
version: 1.0
css:
theme:
css/name-compact.css: {}
js:
js/name-compact.js: {}
dependencies:
- core/drupal
- core/drupalSettings
name-grid:
version: 1.0
css:
theme:
css/name-grid.css: {}
dependencies:
- core/drupal
CSS Example (css/name-inline.css)¶
.name-widget-inline {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.name-widget-inline .form-item {
flex: 1 1 auto;
min-width: 150px;
margin-bottom: 0;
}
.name-widget-inline .form-item--title {
flex: 0 0 100px;
}
.name-widget-inline .form-item--given,
.name-widget-inline .form-item--family {
flex: 1 1 200px;
}
.name-widget-inline .form-item--middle {
flex: 0 0 150px;
}
CSS Example (css/name-two-column.css)¶
.name-widget-two-column {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
column-gap: 2rem;
}
.name-widget-two-column .form-item--title {
grid-column: 1;
}
.name-widget-two-column .form-item--given {
grid-column: 1;
}
.name-widget-two-column .form-item--middle {
grid-column: 2;
grid-row: 2;
}
.name-widget-two-column .form-item--family {
grid-column: 2;
}
Using Custom Layouts¶
Once defined, custom layouts appear in the field widget settings:
- Navigate to:
Structure > Content types > [Type] > Manage form display - Click settings for the name field
- Select your custom layout from the "Layout" dropdown
Default Layout¶
The Name module provides a default "stacked" layout. Custom layouts supplement this default:
// Default layout (provided by the Name module)
'default' => [
'label' => t('Stacked'),
'library' => [],
'wrapper_attributes' => [],
],
Layout Discovery¶
Layouts are discovered when:
- The
name_widget_layouts()helper function is called - Which invokes
hook_name_widget_layouts()on all modules - Results are cached using
drupal_static()
// In name.module
function name_widget_layouts() {
$layouts = &drupal_static(__FUNCTION__);
if (!isset($layouts)) {
$layouts = \Drupal::moduleHandler()
->invokeAll('name_widget_layouts');
// Process and cache layouts
}
return $layouts;
}
Best Practices¶
Responsive Design¶
Make layouts responsive:
'responsive' => [
'label' => t('Responsive'),
'library' => ['my_module/name-responsive'],
'wrapper_attributes' => [
'class' => ['name-widget-responsive'],
],
],
/* Mobile first */
.name-widget-responsive {
display: block;
}
.name-widget-responsive .form-item {
margin-bottom: 1rem;
}
/* Tablet and up */
@media (min-width: 768px) {
.name-widget-responsive {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1rem;
}
}
/* Desktop */
@media (min-width: 1024px) {
.name-widget-responsive {
grid-template-columns: repeat(3, 1fr);
}
}
Accessibility¶
Ensure layouts maintain accessibility:
/* Don't hide labels visually */
.name-widget-compact label {
/* Good: visually hidden but accessible */
position: absolute;
clip: rect(1px, 1px, 1px, 1px);
overflow: hidden;
}
/* Maintain focus indicators */
.name-widget-inline input:focus {
outline: 2px solid var(--color-focus);
outline-offset: 2px;
}
RTL Support¶
Support right-to-left languages:
.name-widget-inline {
display: flex;
}
[dir="rtl"] .name-widget-inline {
flex-direction: row-reverse;
}
Testing Custom Layouts¶
namespace Drupal\Tests\my_module\Functional;
use Drupal\Tests\BrowserTestBase;
class CustomLayoutTest extends BrowserTestBase {
protected static $modules = ['name', 'my_module'];
public function testCustomLayoutAvailable() {
$layouts = name_widget_layouts();
$this->assertArrayHasKey('inline', $layouts);
$this->assertEquals('Inline', $layouts['inline']['label']);
}
public function testLayoutLibraryAttached() {
// Create node with name field
$this->drupalGet('node/add/person');
// Check that custom library is attached
$this->assertSession()->responseContains('my_module/name-inline');
}
}
See Also¶
- NameWidget Class - The field widget that uses layouts
- Form Element - The Name form element
- Extending Guide - Creating custom widgets
- Helper Functions -
name_widget_layouts()function