Material Design CSS framework with interactive JavaScript components for building responsive web applications
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
JavaScript-powered components providing rich interactive functionality including modal dialogs, dropdowns, tabs, collapsibles, and other dynamic UI elements with consistent Material Design styling and behavior.
Modal dialog component for overlaying content with backdrop and customizable animations.
/**
* Modal dialog component
* @param el - Modal element (usually with .modal class)
* @param options - Configuration options
*/
class Modal {
constructor(el: Element, options?: ModalOptions);
/** Initialize modal instances */
static init(els: Element | NodeList, options?: ModalOptions): Modal | Modal[];
/** Get existing modal instance */
static getInstance(el: Element): Modal;
/** Get default options */
static get defaults(): ModalOptions;
/** Open the modal */
open(trigger?: Element): void;
/** Close the modal */
close(): void;
/** Destroy modal instance */
destroy(): void;
/** Current open state */
isOpen: boolean;
/** Modal element */
el: Element;
/** Configuration options */
options: ModalOptions;
}
interface ModalOptions {
/** Overlay opacity (0-1) */
opacity?: number; // default: 0.5
/** Enter animation duration in milliseconds */
inDuration?: number; // default: 250
/** Exit animation duration in milliseconds */
outDuration?: number; // default: 250
/** Callback before modal opens */
onOpenStart?: () => void;
/** Callback after modal opens */
onOpenEnd?: () => void;
/** Callback before modal closes */
onCloseStart?: () => void;
/** Callback after modal closes */
onCloseEnd?: () => void;
/** Prevent background page scrolling when modal is open */
preventScrolling?: boolean; // default: true
/** Allow modal to be dismissed by clicking overlay or pressing ESC */
dismissible?: boolean; // default: true
/** Starting vertical position */
startingTop?: string; // default: "4%"
/** Ending vertical position */
endingTop?: string; // default: "10%"
}Usage Examples:
<!-- Modal HTML structure -->
<div id="modal1" class="modal">
<div class="modal-content">
<h4>Modal Header</h4>
<p>Modal content goes here.</p>
</div>
<div class="modal-footer">
<a href="#!" class="modal-close waves-effect btn-flat">Agree</a>
</div>
</div>
<!-- Modal trigger -->
<a class="waves-effect waves-light btn modal-trigger" href="#modal1">Launch Modal</a>// Initialize modal
const elems = document.querySelectorAll('.modal');
const instances = M.Modal.init(elems, {
opacity: 0.7,
dismissible: false,
onOpenEnd: () => console.log('Modal opened')
});
// Programmatic control
const instance = M.Modal.getInstance(document.getElementById('modal1'));
instance.open();Dropdown menu component for creating interactive dropdown lists and menus.
/**
* Dropdown menu component
* @param el - Trigger element (usually a button)
* @param options - Configuration options
*/
class Dropdown {
constructor(el: Element, options?: DropdownOptions);
static init(els: Element | NodeList, options?: DropdownOptions): Dropdown | Dropdown[];
static getInstance(el: Element): Dropdown;
static get defaults(): DropdownOptions;
/** Open dropdown */
open(): void;
/** Close dropdown */
close(): void;
/** Recalculate dropdown dimensions */
recalculateDimensions(): void;
destroy(): void;
/** Current open state */
isOpen: boolean;
/** Indicates if dropdown content is scrollable */
isScrollable: boolean;
/** Touch movement state tracking */
isTouchMoving: boolean;
/** Currently focused item index for keyboard navigation */
focusedIndex: number;
/** Current filter query for keyboard navigation */
filterQuery: string[];
/** Dropdown element */
el: Element;
/** Dropdown content element */
dropdownEl: Element;
/** Configuration options */
options: DropdownOptions;
}
interface DropdownOptions {
/** Dropdown alignment relative to trigger */
alignment?: 'left' | 'right'; // default: 'left'
/** Automatically focus dropdown when opened */
autoFocus?: boolean; // default: true
/** Constrain dropdown width to trigger width */
constrainWidth?: boolean; // default: true
/** Container element for dropdown */
container?: Element; // default: document.body
/** Position dropdown over trigger */
coverTrigger?: boolean; // default: true
/** Close dropdown when item is clicked */
closeOnClick?: boolean; // default: true
/** Open dropdown on hover instead of click */
hover?: boolean; // default: false
/** Enter animation duration */
inDuration?: number; // default: 150
/** Exit animation duration */
outDuration?: number; // default: 250
/** Callbacks */
onOpenStart?: () => void;
onOpenEnd?: () => void;
onCloseStart?: () => void;
onCloseEnd?: () => void;
onItemClick?: (item: Element) => void;
}Usage Examples:
<!-- Dropdown structure -->
<a class='dropdown-trigger btn' href='#' data-target='dropdown1'>Drop Me!</a>
<ul id='dropdown1' class='dropdown-content'>
<li><a href="#!">One</a></li>
<li><a href="#!">Two</a></li>
<li class="divider" tabindex="-1"></li>
<li><a href="#!">Three</a></li>
</ul>// Initialize dropdown
const elems = document.querySelectorAll('.dropdown-trigger');
const instances = M.Dropdown.init(elems, {
coverTrigger: false,
alignment: 'right'
});Tab navigation component for organizing content into tabbed interfaces.
/**
* Tab navigation component
* @param el - Tabs container element
* @param options - Configuration options
*/
class Tabs {
constructor(el: Element, options?: TabsOptions);
static init(els: Element | NodeList, options?: TabsOptions): Tabs | Tabs[];
static getInstance(el: Element): Tabs;
static get defaults(): TabsOptions;
/** Select tab by ID */
select(tabId: string): void;
/** Update tab indicator position */
updateTabIndicator(): void;
destroy(): void;
}
interface TabsOptions {
/** Tab change animation duration */
duration?: number; // default: 300
/** Callback when tab is shown */
onShow?: (tab: Element) => void;
/** Enable swipeable tabs */
swipeable?: boolean; // default: false
/** Responsive threshold for tab behavior */
responsiveThreshold?: number; // default: Infinity
}Usage Examples:
<!-- Tabs structure -->
<div class="row">
<div class="col s12">
<ul class="tabs">
<li class="tab col s3"><a href="#test1">Test 1</a></li>
<li class="tab col s3"><a class="active" href="#test2">Test 2</a></li>
<li class="tab col s3"><a href="#test3">Test 3</a></li>
</ul>
</div>
<div id="test1" class="col s12">Test 1 content</div>
<div id="test2" class="col s12">Test 2 content</div>
<div id="test3" class="col s12">Test 3 content</div>
</div>// Initialize tabs
const elems = document.querySelectorAll('.tabs');
const instances = M.Tabs.init(elems, {
swipeable: true,
onShow: (tab) => console.log('Tab shown:', tab.id)
});
// Programmatic tab selection
const instance = M.Tabs.getInstance(document.querySelector('.tabs'));
instance.select('test2');Expandable/accordion component for organizing content in collapsible sections.
/**
* Collapsible/accordion component
* @param el - Collapsible container element
* @param options - Configuration options
*/
class Collapsible {
constructor(el: Element, options?: CollapsibleOptions);
static init(els: Element | NodeList, options?: CollapsibleOptions): Collapsible | Collapsible[];
static getInstance(el: Element): Collapsible;
static get defaults(): CollapsibleOptions;
/** Open collapsible section by index */
open(index: number): void;
/** Close collapsible section by index */
close(index: number): void;
destroy(): void;
}
interface CollapsibleOptions {
/** Accordion mode (only one section open) vs expandable (multiple open) */
accordion?: boolean; // default: true
/** Callback before section opens */
onOpenStart?: (section: Element) => void;
/** Callback after section opens */
onOpenEnd?: (section: Element) => void;
/** Callback before section closes */
onCloseStart?: (section: Element) => void;
/** Callback after section closes */
onCloseEnd?: (section: Element) => void;
/** Enter animation duration */
inDuration?: number; // default: 300
/** Exit animation duration */
outDuration?: number; // default: 300
}Usage Examples:
<!-- Collapsible structure -->
<ul class="collapsible">
<li>
<div class="collapsible-header"><i class="material-icons">filter_drama</i>First</div>
<div class="collapsible-body"><span>Lorem ipsum dolor sit amet.</span></div>
</li>
<li class="active">
<div class="collapsible-header"><i class="material-icons">place</i>Second</div>
<div class="collapsible-body"><span>Lorem ipsum dolor sit amet.</span></div>
</li>
</ul>// Initialize collapsible
const elems = document.querySelectorAll('.collapsible');
const instances = M.Collapsible.init(elems, {
accordion: false // Allow multiple sections open
});Notification toast component for displaying temporary messages to users.
/**
* Toast notification component
* Note: Toast does not extend Component base class
* @param options - Toast configuration
*/
class Toast {
constructor(options: ToastOptions);
/** Dismiss all active toasts */
static dismissAll(): void;
/** Dismiss this toast */
dismiss(): void;
}
interface ToastOptions {
/** Toast message HTML content */
html: string;
/** Display duration in milliseconds */
displayLength?: number; // default: 4000
/** Enter animation duration */
inDuration?: number; // default: 300
/** Exit animation duration */
outDuration?: number; // default: 375
/** Additional CSS classes */
classes?: string;
/** Callback when toast is dismissed */
completeCallback?: () => void;
/** Percentage of drag distance to activate dismissal */
activationPercent?: number; // default: 0.8
}
// Convenience method on M object
declare const M: {
toast(options: ToastOptions | { html: string }): Toast;
};Usage Examples:
// Simple toast
M.toast({html: 'I am a toast!'});
// Styled toast with callback
M.toast({
html: 'Success! Data saved.',
classes: 'green',
displayLength: 2000,
completeCallback: () => console.log('Toast dismissed')
});
// Toast with action button
M.toast({
html: `<span>Message sent</span><button class="btn-flat toast-action">Undo</button>`,
classes: 'rounded'
});Tooltip component for displaying contextual information on hover or focus.
/**
* Tooltip component
* @param el - Element that triggers tooltip
* @param options - Configuration options
*/
class Tooltip {
constructor(el: Element, options?: TooltipOptions);
static init(els: Element | NodeList, options?: TooltipOptions): Tooltip | Tooltip[];
static getInstance(el: Element): Tooltip;
static get defaults(): TooltipOptions;
/** Open tooltip manually */
open(isManual?: boolean): void;
/** Close tooltip manually */
close(): void;
destroy(): void;
/** Current open state */
isOpen: boolean;
/** Tooltip element */
el: Element;
/** Configuration options */
options: TooltipOptions;
}
interface TooltipOptions {
/** Delay before hiding tooltip */
exitDelay?: number; // default: 0
/** Delay before showing tooltip */
enterDelay?: number; // default: 200
/** Tooltip HTML content */
html?: string;
/** Margin from trigger element */
margin?: number; // default: 5
/** Enter animation duration */
inDuration?: number; // default: 300
/** Exit animation duration */
outDuration?: number; // default: 250
/** Tooltip position relative to trigger */
position?: 'top' | 'right' | 'bottom' | 'left'; // default: 'bottom'
/** Movement distance during animation */
transitionMovement?: number; // default: 10
}Usage Examples:
<!-- Tooltip via data attribute -->
<a class="btn tooltipped" data-position="bottom" data-tooltip="I am a tooltip">Hover me!</a>
<!-- Tooltip via HTML content -->
<a class="btn tooltipped" data-position="top" data-html="true"
data-tooltip="<b>Bold</b> tooltip content">HTML Tooltip</a>// Initialize tooltips
const elems = document.querySelectorAll('.tooltipped');
const instances = M.Tooltip.init(elems, {
position: 'right',
enterDelay: 500
});
// Manual tooltip control
const tooltip = M.Tooltip.init(document.querySelector('#help-button'));
// Programmatically open/close tooltip
tooltip.open(true); // Pass true for manual opening
setTimeout(() => {
tooltip.close();
}, 3000);
// Check tooltip state
if (tooltip.isOpen) {
console.log('Tooltip is currently open');
}Floating Action Button with expandable menu for primary actions.
/**
* Floating Action Button component
* @param el - FAB container element
* @param options - Configuration options
*/
class FloatingActionButton {
constructor(el: Element, options?: FloatingActionButtonOptions);
static init(els: Element | NodeList, options?: FloatingActionButtonOptions): FloatingActionButton | FloatingActionButton[];
static getInstance(el: Element): FloatingActionButton;
static get defaults(): FloatingActionButtonOptions;
/** Open FAB menu */
open(): void;
/** Close FAB menu */
close(): void;
destroy(): void;
}
interface FloatingActionButtonOptions {
/** Direction of menu expansion */
direction?: 'top' | 'right' | 'bottom' | 'left'; // default: 'top'
/** Enable hover to open instead of click */
hoverEnabled?: boolean; // default: true
/** Enable toolbar transformation */
toolbarEnabled?: boolean; // default: false
}Usage Examples:
<!-- FAB structure -->
<div class="fixed-action-btn">
<a class="btn-floating btn-large red">
<i class="large material-icons">mode_edit</i>
</a>
<ul>
<li><a class="btn-floating red"><i class="material-icons">insert_chart</i></a></li>
<li><a class="btn-floating yellow darken-1"><i class="material-icons">format_quote</i></a></li>
<li><a class="btn-floating green"><i class="material-icons">publish</i></a></li>
<li><a class="btn-floating blue"><i class="material-icons">attach_file</i></a></li>
</ul>
</div>// Initialize FAB
const elems = document.querySelectorAll('.fixed-action-btn');
const instances = M.FloatingActionButton.init(elems, {
direction: 'left',
hoverEnabled: false
});// Auto-initialization (recommended for most cases)
document.addEventListener('DOMContentLoaded', function() {
M.AutoInit();
});
// Manual initialization with options
const elems = document.querySelectorAll('.component-selector');
const instances = M.ComponentName.init(elems, {
// Component-specific options
});
// Individual instance creation
const elem = document.getElementById('my-component');
const instance = new M.ComponentName(elem, options);// Using callback options
const modal = M.Modal.init(elem, {
onOpenStart: () => console.log('Modal opening...'),
onOpenEnd: () => console.log('Modal opened'),
onCloseStart: () => console.log('Modal closing...'),
onCloseEnd: () => console.log('Modal closed')
});
// Custom event listeners
elem.addEventListener('click', function() {
const instance = M.Modal.getInstance(this);
if (instance.isOpen) {
instance.close();
} else {
instance.open();
}
});Pushpin component for creating sticky elements that change behavior based on scroll position, ideal for navigation headers, sidebars, and content that should remain visible during scrolling.
/**
* Pushpin component for sticky positioning behavior
* @param el - Element to make sticky (usually with .pushpin class)
* @param options - Configuration options
*/
class Pushpin {
constructor(el: Element, options?: PushpinOptions);
/** Initialize pushpin instances */
static init(els: Element | NodeList, options?: PushpinOptions): Pushpin | Pushpin[];
/** Get existing pushpin instance */
static getInstance(el: Element): Pushpin;
/** Get default options */
static get defaults(): PushpinOptions;
/** Destroy pushpin instance */
destroy(): void;
/** Pushpin element */
el: Element;
/** Configuration options */
options: PushpinOptions;
/** Original element offset from top */
originalOffset: number;
}
interface PushpinOptions {
/** Top offset position for pin activation */
top?: number; // default: 0
/** Bottom offset position for pin deactivation */
bottom?: number; // default: Infinity
/** Additional offset from scroll position */
offset?: number; // default: 0
/** Callback fired when pin position changes */
onPositionChange?: (position: 'pin-top' | 'pinned' | 'pin-bottom') => void;
}Usage Examples:
<!-- Basic pushpin element -->
<div class="pushpin">
<h4>This content will stick</h4>
<p>Navigation or content that should remain visible</p>
</div>
<!-- Pushpin with data attributes -->
<nav class="pushpin" data-target="nav-demo">
<div class="nav-wrapper">
<a href="#" class="brand-logo">Logo</a>
</div>
</nav>// Initialize pushpin
const elems = document.querySelectorAll('.pushpin');
const instances = M.Pushpin.init(elems, {
top: 400,
bottom: 1000,
offset: 20,
onPositionChange: function(position) {
console.log('Pushpin position changed to:', position);
}
});
// Manual initialization with specific boundaries
const navbar = document.querySelector('#sticky-nav');
M.Pushpin.init(navbar, {
top: navbar.offsetTop,
bottom: document.body.scrollHeight - navbar.offsetHeight,
offset: 0
});CSS Classes Applied:
.pin-top - Applied when scroll position is above the top threshold.pinned - Applied when element is actively pinned (between top and bottom).pin-bottom - Applied when scroll position is below the bottom thresholdTapTarget component for feature discovery and highlighting, creating animated overlays that guide users to specific elements or features in your application.
/**
* TapTarget component for feature discovery and user onboarding
* @param el - TapTarget element (usually with .tap-target class)
* @param options - Configuration options
*/
class TapTarget {
constructor(el: Element, options?: TapTargetOptions);
/** Initialize tap target instances */
static init(els: Element | NodeList, options?: TapTargetOptions): TapTarget | TapTarget[];
/** Get existing tap target instance */
static getInstance(el: Element): TapTarget;
/** Get default options */
static get defaults(): TapTargetOptions;
/** Open the tap target */
open(): void;
/** Close the tap target */
close(): void;
/** Destroy tap target instance */
destroy(): void;
/** Current open state */
isOpen: boolean;
/** TapTarget element */
el: Element;
/** Configuration options */
options: TapTargetOptions;
/** Target element wrapper */
wrapper: Element;
/** Wave animation element */
waveEl: Element;
/** Origin element (the element being highlighted) */
originEl: Element;
/** Content container element */
contentEl: Element;
}
interface TapTargetOptions {
/** Callback fired when tap target opens */
onOpen?: (origin: Element) => void;
/** Callback fired when tap target closes */
onClose?: (origin: Element) => void;
}Usage Examples:
<!-- Target element to highlight -->
<a id="menu-button" class="btn-floating btn-large red">
<i class="material-icons">menu</i>
</a>
<!-- Tap target overlay -->
<div class="tap-target" data-target="menu-button">
<div class="tap-target-content">
<h5>Menu Button</h5>
<p>Click this button to open the navigation menu and access all features.</p>
</div>
</div>
<!-- Multiple tap targets for onboarding flow -->
<div class="tap-target" data-target="search-input">
<div class="tap-target-content">
<h5>Search</h5>
<p>Use this search bar to find what you're looking for quickly.</p>
</div>
</div>// Initialize tap targets
const elems = document.querySelectorAll('.tap-target');
const instances = M.TapTarget.init(elems, {
onOpen: function(origin) {
console.log('Tap target opened for:', origin);
},
onClose: function(origin) {
console.log('Tap target closed for:', origin);
}
});
// Manual control for onboarding flow
const menuTarget = M.TapTarget.getInstance(document.querySelector('[data-target="menu-button"]'));
const searchTarget = M.TapTarget.getInstance(document.querySelector('[data-target="search-input"]'));
// Start onboarding sequence
function startOnboarding() {
menuTarget.open();
// After user interacts, show next step
setTimeout(() => {
menuTarget.close();
searchTarget.open();
}, 3000);
}
// Programmatically open/close
const tapTarget = document.querySelector('.tap-target');
const instance = M.TapTarget.init(tapTarget);
// Open tap target
instance.open();
// Close after delay
setTimeout(() => instance.close(), 5000);Features:
// Get existing instance
const instance = M.ComponentName.getInstance(element);
// Check instance state
if (instance && instance.isOpen) {
instance.close();
}
// Cleanup when component no longer needed
instance.destroy();
// Reinitialize after DOM changes
M.ComponentName.init(newElements, options);