0
# Layout Components
1
2
Structural components for application layout including headers, drawers, navigation, tabs, and responsive grid systems. These components provide the foundation for Material Design app structures with responsive behavior and navigation patterns.
3
4
## Capabilities
5
6
### Material Layout
7
8
Main layout component that provides app structure with header, drawer, and content areas.
9
10
```javascript { .api }
11
/**
12
* Material Design layout component
13
* CSS Class: mdl-js-layout
14
* Widget: false
15
*/
16
interface MaterialLayout {
17
/** Toggle drawer open/closed state */
18
toggleDrawer(): void;
19
}
20
```
21
22
**HTML Structure:**
23
24
```html
25
<!-- Basic layout with header and drawer -->
26
<div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
27
<header class="mdl-layout__header">
28
<div class="mdl-layout__header-row">
29
<!-- Title -->
30
<span class="mdl-layout-title">Title</span>
31
<!-- Add spacer, to align navigation to the right -->
32
<div class="mdl-layout-spacer"></div>
33
<!-- Navigation. We hide it in small screens. -->
34
<nav class="mdl-navigation mdl-layout--large-screen-only">
35
<a class="mdl-navigation__link" href="">Link</a>
36
<a class="mdl-navigation__link" href="">Link</a>
37
</nav>
38
</div>
39
</header>
40
41
<div class="mdl-layout__drawer">
42
<span class="mdl-layout-title">Title</span>
43
<nav class="mdl-navigation">
44
<a class="mdl-navigation__link" href="">Link</a>
45
<a class="mdl-navigation__link" href="">Link</a>
46
</nav>
47
</div>
48
49
<main class="mdl-layout__content">
50
<div class="page-content">
51
<!-- Your content goes here -->
52
</div>
53
</main>
54
</div>
55
```
56
57
**Layout Modes:**
58
59
```html
60
<!-- Fixed header that stays at top during scroll -->
61
<div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
62
63
<!-- Fixed drawer that stays open on large screens -->
64
<div class="mdl-layout mdl-js-layout mdl-layout--fixed-drawer">
65
66
<!-- Waterfall header that shrinks on scroll -->
67
<div class="mdl-layout mdl-js-layout mdl-layout--fixed-header mdl-layout--waterfall">
68
69
<!-- No drawer spacer -->
70
<div class="mdl-layout mdl-js-layout mdl-layout--no-drawer-button">
71
72
<!-- No desktop drawer -->
73
<div class="mdl-layout mdl-js-layout mdl-layout--no-desktop-drawer-button">
74
```
75
76
**Usage Examples:**
77
78
```javascript
79
// Access layout instance
80
const layout = document.querySelector('.mdl-js-layout').MaterialLayout;
81
82
// Toggle drawer programmatically
83
layout.toggleDrawer();
84
85
// Open drawer on button click
86
document.querySelector('#menu-button').addEventListener('click', () => {
87
layout.toggleDrawer();
88
});
89
90
// Close drawer when clicking on content
91
document.querySelector('.mdl-layout__content').addEventListener('click', () => {
92
const drawer = document.querySelector('.mdl-layout__drawer');
93
if (drawer.classList.contains('is-visible')) {
94
layout.toggleDrawer();
95
}
96
});
97
```
98
99
### Material Layout Tab
100
101
Individual tab component within a layout header tab bar.
102
103
```javascript { .api }
104
/**
105
* Material Design layout tab component
106
* CSS Class: mdl-js-layout (parent layout manages tabs)
107
* Widget: true (as MaterialLayoutTab)
108
*/
109
interface MaterialLayoutTab {
110
/** Programmatically select this tab and show associated panel */
111
show(): void;
112
}
113
```
114
115
**HTML Structure:**
116
117
```html
118
<div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
119
<header class="mdl-layout__header">
120
<div class="mdl-layout__header-row">
121
<span class="mdl-layout-title">Title</span>
122
</div>
123
<!-- Tabs -->
124
<div class="mdl-layout__tab-bar mdl-js-ripple-effect">
125
<a href="#scroll-tab-1" class="mdl-layout__tab is-active">Tab 1</a>
126
<a href="#scroll-tab-2" class="mdl-layout__tab">Tab 2</a>
127
<a href="#scroll-tab-3" class="mdl-layout__tab">Tab 3</a>
128
</div>
129
</header>
130
131
<main class="mdl-layout__content">
132
<section class="mdl-layout__tab-panel is-active" id="scroll-tab-1">
133
<div class="page-content"><!-- Your content goes here --></div>
134
</section>
135
<section class="mdl-layout__tab-panel" id="scroll-tab-2">
136
<div class="page-content"><!-- Your content goes here --></div>
137
</section>
138
<section class="mdl-layout__tab-panel" id="scroll-tab-3">
139
<div class="page-content"><!-- Your content goes here --></div>
140
</section>
141
</main>
142
</div>
143
```
144
145
**Usage Examples:**
146
147
```javascript
148
// Access tab instances
149
const tabs = document.querySelectorAll('.mdl-layout__tab');
150
const tabInstances = Array.from(tabs).map(tab => tab.MaterialLayoutTab);
151
152
// Programmatically switch to a specific tab
153
const secondTab = tabs[1].MaterialLayoutTab;
154
secondTab.show();
155
156
// Dynamic tab switching
157
function switchToTab(index) {
158
const tab = tabs[index];
159
if (tab && tab.MaterialLayoutTab) {
160
tab.MaterialLayoutTab.show();
161
}
162
}
163
164
// Listen for tab changes
165
document.addEventListener('click', (event) => {
166
if (event.target.matches('.mdl-layout__tab')) {
167
console.log('Tab clicked:', event.target.textContent);
168
}
169
});
170
```
171
172
### Material Tabs
173
174
Standalone tab component for organizing content into switchable sections.
175
176
```javascript { .api }
177
/**
178
* Material Design tabs component
179
* CSS Class: mdl-js-tabs
180
* Widget: false
181
*/
182
interface MaterialTabs {
183
// No public methods - behavior is entirely automatic
184
// Tabs are activated by clicking tab elements
185
}
186
```
187
188
**HTML Structure:**
189
190
```html
191
<!-- Standalone tabs -->
192
<div class="mdl-tabs mdl-js-tabs mdl-js-ripple-effect">
193
<div class="mdl-tabs__tab-bar">
194
<a href="#starks-panel" class="mdl-tabs__tab is-active">Starks</a>
195
<a href="#lannisters-panel" class="mdl-tabs__tab">Lannisters</a>
196
<a href="#targaryens-panel" class="mdl-tabs__tab">Targaryens</a>
197
</div>
198
199
<div class="mdl-tabs__panel is-active" id="starks-panel">
200
<ul>
201
<li>Eddard</li>
202
<li>Catelyn</li>
203
<li>Robb</li>
204
</ul>
205
</div>
206
<div class="mdl-tabs__panel" id="lannisters-panel">
207
<ul>
208
<li>Tywin</li>
209
<li>Cersei</li>
210
<li>Jaime</li>
211
</ul>
212
</div>
213
<div class="mdl-tabs__panel" id="targaryens-panel">
214
<ul>
215
<li>Viserys</li>
216
<li>Daenerys</li>
217
</ul>
218
</div>
219
</div>
220
```
221
222
**Usage Examples:**
223
224
```javascript
225
// Tabs work automatically, but you can listen for changes
226
document.addEventListener('click', (event) => {
227
if (event.target.matches('.mdl-tabs__tab')) {
228
const targetPanel = event.target.getAttribute('href');
229
console.log('Switching to panel:', targetPanel);
230
231
// Perform custom actions when tab changes
232
onTabChange(targetPanel);
233
}
234
});
235
236
function onTabChange(panelId) {
237
// Custom logic for tab changes
238
const panel = document.querySelector(panelId);
239
if (panel) {
240
// Load content dynamically if needed
241
loadPanelContent(panel);
242
}
243
}
244
245
// Programmatically activate a tab
246
function activateTab(tabSelector) {
247
const tab = document.querySelector(tabSelector);
248
const panel = document.querySelector(tab.getAttribute('href'));
249
250
// Remove active class from all tabs and panels
251
document.querySelectorAll('.mdl-tabs__tab').forEach(t => t.classList.remove('is-active'));
252
document.querySelectorAll('.mdl-tabs__panel').forEach(p => p.classList.remove('is-active'));
253
254
// Add active class to selected tab and panel
255
tab.classList.add('is-active');
256
panel.classList.add('is-active');
257
}
258
```
259
260
## Layout Constants
261
262
```javascript { .api }
263
/**
264
* Material Layout constants and configuration
265
*/
266
interface LayoutConstants {
267
/** Media query for maximum width */
268
MAX_WIDTH: '(max-width: 1024px)';
269
270
/** Pixels to scroll tabs when using arrow buttons */
271
TAB_SCROLL_PIXELS: 100;
272
273
/** Timeout for resize event handling */
274
RESIZE_TIMEOUT: 100;
275
276
/** Menu icon HTML entity */
277
MENU_ICON: '';
278
279
/** Left chevron icon name */
280
CHEVRON_LEFT: 'chevron_left';
281
282
/** Right chevron icon name */
283
CHEVRON_RIGHT: 'chevron_right';
284
}
285
286
/**
287
* Layout modes
288
*/
289
interface LayoutModes {
290
/** Standard layout mode */
291
STANDARD: 0;
292
293
/** Seamed layout mode */
294
SEAMED: 1;
295
296
/** Waterfall layout mode */
297
WATERFALL: 2;
298
299
/** Scroll layout mode */
300
SCROLL: 3;
301
}
302
303
/**
304
* Keyboard codes used by layout components
305
*/
306
interface LayoutKeyCodes {
307
ENTER: 13;
308
ESCAPE: 27;
309
SPACE: 32;
310
}
311
```
312
313
## Responsive Behavior
314
315
Layout components automatically adapt to different screen sizes:
316
317
```javascript
318
// Monitor responsive changes
319
const mediaQuery = window.matchMedia('(max-width: 1024px)');
320
321
mediaQuery.addListener((mq) => {
322
if (mq.matches) {
323
// Mobile/tablet view
324
console.log('Switched to mobile layout');
325
setupMobileNavigation();
326
} else {
327
// Desktop view
328
console.log('Switched to desktop layout');
329
setupDesktopNavigation();
330
}
331
});
332
333
function setupMobileNavigation() {
334
// Show drawer button
335
// Hide large-screen-only navigation
336
}
337
338
function setupDesktopNavigation() {
339
// Hide drawer button (if no-desktop-drawer-button class is used)
340
// Show large-screen navigation
341
}
342
```
343
344
## Layout Integration
345
346
```javascript
347
// Initialize layout with custom behavior
348
function initializeLayout() {
349
const layout = document.querySelector('.mdl-js-layout');
350
351
// Ensure layout is upgraded
352
componentHandler.upgradeElement(layout);
353
354
// Set up drawer close on content click
355
const content = layout.querySelector('.mdl-layout__content');
356
content.addEventListener('click', () => {
357
const drawer = layout.querySelector('.mdl-layout__drawer');
358
if (drawer.classList.contains('is-visible')) {
359
layout.MaterialLayout.toggleDrawer();
360
}
361
});
362
363
// Set up tab switching
364
const tabs = layout.querySelectorAll('.mdl-layout__tab');
365
tabs.forEach((tab, index) => {
366
tab.addEventListener('click', () => {
367
console.log('Layout tab clicked:', index);
368
// Custom tab switching logic
369
});
370
});
371
}
372
373
// Handle dynamic content updates
374
function updateLayoutContent(newContent) {
375
const contentArea = document.querySelector('.mdl-layout__content .page-content');
376
contentArea.innerHTML = newContent;
377
378
// Upgrade any new MDL components in the content
379
componentHandler.upgradeElements(contentArea.querySelectorAll('[class*="mdl-js-"]'));
380
}
381
```
382
383
## Accessibility
384
385
Layout components include accessibility features:
386
387
```javascript
388
// Keyboard navigation support
389
document.addEventListener('keydown', (event) => {
390
const layout = document.querySelector('.mdl-js-layout').MaterialLayout;
391
392
switch (event.key) {
393
case 'Escape':
394
// Close drawer on escape
395
const drawer = document.querySelector('.mdl-layout__drawer');
396
if (drawer.classList.contains('is-visible')) {
397
layout.toggleDrawer();
398
}
399
break;
400
401
case 'Tab':
402
// Handle tab navigation within drawer
403
if (event.target.closest('.mdl-layout__drawer')) {
404
handleDrawerTabNavigation(event);
405
}
406
break;
407
}
408
});
409
410
function handleDrawerTabNavigation(event) {
411
const drawer = document.querySelector('.mdl-layout__drawer');
412
const focusableElements = drawer.querySelectorAll('a, button, [tabindex]:not([tabindex="-1"])');
413
const firstElement = focusableElements[0];
414
const lastElement = focusableElements[focusableElements.length - 1];
415
416
if (event.shiftKey && event.target === firstElement) {
417
// Wrap to last element
418
event.preventDefault();
419
lastElement.focus();
420
} else if (!event.shiftKey && event.target === lastElement) {
421
// Wrap to first element
422
event.preventDefault();
423
firstElement.focus();
424
}
425
}
426
```