Jesse A. Green / notebook

Materialize Admin & Developer Guide

Generated June 2026 from Dogfalo/materialize at 824e782

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

This guide covers Materialize from the perspective of architects, build engineers, and contributors: how the framework is structured, how it is compiled, how to customize it through Sass, what its security posture is, and how to extend or contribute to it. It complements the User Guide, which covers day-to-day usage of components and the grid.

All facts here are derived from the framework's source at version 1.0.0.


1. Executive Summary

Materialize is a responsive front-end CSS/JavaScript framework that implements Google's Material Design specification. It is consumed by web developers as two compiled artifacts — materialize.css and materialize.js — produced from Sass and ES2015 JavaScript sources by a Grunt build pipeline.

Attribute Value
Type Front-end CSS/JS framework
Version 1.0.0
License MIT
npm package materialize-css
Bower / Meteor package materialize / materialize:materialize
Runtime dependencies None (cash + anime.js are bundled)
Build system Grunt (Sass, Babel, PostCSS, UglifyJS, Jade)
Test framework Jasmine + jasmine-jquery (PhantomJS)
Distribution entry dist/js/materialize.js, dist/css/materialize.css
Sass entry sass/materialize.scss

Important

The Dogfalo/materialize repository is no longer actively maintained (issues #6615, #6691) and has unresolved security advisories (issue #6331: CVE-2019-11002, CVE-2019-11003, CVE-2019-11004). Factor this into adoption and patching decisions — see Section 6.


2. Architecture

2.1 High-level structure

Materialize is two parallel subsystems — a style layer (Sass → CSS) and a behavior layer (ES2015 → bundled JS) — plus a documentation site and starter templates.

+-----------------------------------------------------------+
|                     Materialize                            |
|                                                           |
|  Style layer (Sass)            Behavior layer (JS)        |
|  +------------------------+    +----------------------+    |
|  | sass/materialize.scss  |    | js/component.js      |    |
|  |   @import components/* |    | js/global.js  (M.*)  |    |
|  |   - variables          |    | js/<component>.js x23|    |
|  |   - grid, typography   |    | js/cash.js (DOM lib) |    |
|  |   - 30+ components      |    | js/anime.min.js      |    |
|  +-----------+------------+    +-----------+----------+    |
|              |  grunt sass               |  grunt concat   |
|              v                           v  + babel + uglify|
|  dist/css/materialize.css      dist/js/materialize.js      |
+-----------------------------------------------------------+
        ^                                  ^
        |  <link>                          |  <script>
        |                                  |
  +-----+----------------------------------+-----+
  |        Consumer web page (your HTML)         |
  +---------------------------------------------+

Supporting (not shipped to consumers):
  jade/        -> documentation site source (compiled to HTML)
  templates/   -> downloadable starter templates
  tests/spec/  -> Jasmine test specs
  extras/      -> vendored noUiSlider

2.2 The JavaScript component model

Every interactive component follows the same pattern, anchored by a shared base class.

Base class — js/component.js:

class Component {
  constructor(classDef, el, options) {
    if (!(el instanceof Element)) {
      console.error(Error(el + ' is not an HTML Element'));
    }
    // Destroy a prior instance on the same element, then re-init
    let ins = classDef.getInstance(el);
    if (!!ins) { ins.destroy(); }
    this.el = el;
    this.$el = cash(el);
  }

  static init(classDef, els, options) {
    // Accepts an Element, NodeList, or jQuery/cash collection
    // Returns one instance or an array of instances
  }
}

Each component (e.g. js/modal.js) then:

  1. Declares a private _defaults options object.
  2. Extends Component and stores its instance on the element as el.M_<Name> (e.g. el.M_Modal).
  3. Implements static get defaults(), static init(els, options), and static getInstance(el).
  4. Implements destroy() plus component-specific public methods (open, close, etc.).
  5. Registers itself on the global M namespace: M.Modal = Modal.
  6. If jQuery is present, registers a jQuery plugin wrapper.

Note

Re-initializing a component on an element that already has an instance automatically destroys the old instance first (handled in the base constructor). This makes init() idempotent and safe to call after dynamic DOM updates.

2.3 The M global namespace — js/global.js

global.js creates the window.M object (or M for Meteor packages) and attaches framework-wide utilities:

Member Purpose
M.version '1.0.0'
M.AutoInit(context?) Initialize all components found in context (default document.body)
M.initializeJqueryWrapper(plugin, name, classRef) Register a jQuery plugin for a component
M.keys Key code map (TAB, ENTER, ESC, ARROW_UP, ARROW_DOWN)
M.tabPressed / M.keyDown Global keyboard-focus state (drives .keyboard-focused on <body>)
M.guid() Generate a unique id
M.throttle(fn, wait, options) Underscore-style throttle
M.escapeHash(hash) Escape special chars in a hash selector
M.getIdFromTrigger(trigger) Resolve a target id from data-target or href
M.getDocumentScrollTop() / M.getDocumentScrollLeft() Cross-browser scroll position
M.getClosestAncestor(el, condition) Walk up the DOM for a matching ancestor
M.checkWithinContainer(...) / M.checkPossibleAlignments(...) Positioning math used by dropdowns/tooltips
M.getOverflowParent(el) Find the nearest non-visible overflow ancestor
M.elementOrParentIsFixed(el) Detect fixed positioning in the ancestor chain

The module also defines AMD and CommonJS exports so Materialize can be imported as a module (exports.default = M).

2.4 Auto-initialization

M.AutoInit() maps each component class to a CSS selector and calls init() on every match that does not carry the no-autoinit class:

let registry = {
  Autocomplete: root.querySelectorAll('.autocomplete:not(.no-autoinit)'),
  Carousel:     root.querySelectorAll('.carousel:not(.no-autoinit)'),
  // … 15 more …
  FormSelect:   root.querySelectorAll('select:not(.no-autoinit)'),
  FloatingActionButton: root.querySelectorAll('.fixed-action-btn:not(.no-autoinit)')
};
for (let pluginName in registry) {
  M[pluginName].init(registry[pluginName]);
}

Because auto-init always uses defaults, components needing custom options should be excluded with no-autoinit and initialized manually. (The full selector table is in the User Guide, Section 6.)

2.5 The jQuery wrapper

M.initializeJqueryWrapper generates a jQuery plugin per component:

jQuery.fn[pluginName] = function(methodOrOptions) {
  if (plugin.prototype[methodOrOptions]) {
    // method call: getters (name starts with "get") return a value;
    // other methods run on each matched element
  } else if (typeof methodOrOptions === 'object' || !methodOrOptions) {
    plugin.init(this, arguments[0]);   // initialization
    return this;
  }
  jQuery.error(`Method ${methodOrOptions} does not exist on jQuery.${pluginName}`);
};

This is only invoked when M.jQueryLoaded is true (!!window.jQuery at load time), so jQuery remains an optional dependency.

2.6 Component inventory

23 JavaScript classes back the interactive components (all extend Component except Toast, which is standalone):

Class Source Trigger selector
Autocomplete js/autocomplete.js .autocomplete
FloatingActionButton js/buttons.js .fixed-action-btn
Carousel js/carousel.js .carousel
CharacterCounter js/characterCounter.js [data-length]
Chips js/chips.js .chips
Collapsible js/collapsible.js .collapsible
Datepicker js/datepicker.js .datepicker
Dropdown js/dropdown.js .dropdown-trigger
Materialbox js/materialbox.js .materialboxed
Modal js/modal.js .modal
Parallax js/parallax.js .parallax
Pushpin js/pushpin.js .pushpin
Range js/range.js input[type=range]
ScrollSpy js/scrollspy.js .scrollspy
FormSelect js/select.js select
Sidenav js/sidenav.js .sidenav
Slider js/slider.js .slider
Tabs js/tabs.js .tabs
TapTarget js/tapTarget.js .tap-target
Timepicker js/timepicker.js .timepicker
Toast js/toasts.js (programmatic via M.toast)
Tooltip js/tooltip.js .tooltipped
Component js/component.js (base class)

Plus non-class behavior modules: js/cards.js (delegated click handler for card reveal), js/forms.js (input/textarea/select enhancements and M.updateTextFields/M.validate_field/M.textareaAutoResize), and js/waves.js (ripple effect).

2.7 Bundled libraries

Library File Role
cash js/cash.js Lightweight jQuery-compatible DOM API ($) used internally by all components
anime.js js/anime.min.js Animation engine, exposed as M.anime
noUiSlider extras/noUiSlider/ Optional standalone range slider, shipped separately under extras/ (not part of the core bundle)

Note

All components are wrapped in IIFEs receiving cash as $ (e.g. (function($, anim){ … })(cash, M.anime)). This means component internals never depend on a global jQuery — only the optional public wrapper does.


3. Requirements

Runtime (consumers)

  • A supported browser: Chrome 35+, Firefox 31+, Safari 9+, Opera, Edge, IE 11+.
  • The Material Icons web font (linked from Google Fonts) for icon glyphs.
  • No JavaScript runtime dependency — jQuery is optional.

Build / development (contributors)

  • Node.js ≥ 6 (declared as "engine": "node >= 6" in package.json).
  • npm (to install dev dependencies).
  • Grunt (installed via dev dependencies; run through npx/local node_modules).

The build chain pulls in (dev dependencies): grunt and its plugins (grunt-sass, grunt-babel, grunt-postcss, grunt-contrib-concat, grunt-contrib-uglify, grunt-contrib-jade, grunt-contrib-jasmine, grunt-contrib-watch, grunt-browser-sync, grunt-contrib-compress, grunt-banner, and more), plus node-sass, autoprefixer, babel-preset-es2015, jasmine, jasmine-jquery, jquery, phantomjs-prebuilt, prettier, husky, and lint-staged.


4. Build & Customization (Sass)

4.1 Sass entry point

sass/materialize.scss imports every component partial in dependency order:

@charset "UTF-8";

// Color
@import "components/color-variables";
@import "components/color-classes";

// Variables
@import "components/variables";

// Reset
@import "components/normalize";

// components
@import "components/global";
@import "components/badges";
@import "components/icons-material-design";
@import "components/grid";
@import "components/navbar";
@import "components/typography";
// … cards, toast, tabs, tooltip, buttons, dropdown, waves, modal,
//    collapsible, chips, materialbox, forms/forms, table_of_contents,
//    sidenav, preloader, slider, carousel, tapTarget, pulse,
//    datepicker, timepicker

To exclude components you don't use, comment out their @import lines and recompile. This is the supported way to slim down the CSS bundle.

4.2 Customizing via variables

All design tokens live in sass/components/_variables.scss and are declared with !default, so you can override any of them before importing Materialize:

// my-theme.scss
$primary-color:   color("blue", "darken-2");
$secondary-color: color("orange", "accent-3");
$num-cols:        12;       // grid columns
$gutter-width:    1.5rem;   // grid gutter

@import "materialize";

Key variable groups (24 sections in _variables.scss):

Group Example variables
Colors $primary-color, $secondary-color, $success-color, $error-color, $link-color
Grid $num-cols (12), $gutter-width (1.5rem)
Breakpoints $small-screen-up (601px), $medium-screen-up (993px), $large-screen-up (1201px) and matching media-query strings ($medium-and-up, etc.)
Buttons $button-height (36px), $button-raised-background, $button-floating-size (40px), $button-radius (2px)
Typography $font-stack (system font stack), $h1-fontsize (4.2rem) … $h6-fontsize (1.15rem), $off-black
Forms $input-height (3rem), $input-border-color, $input-focus-color, $radio-fill-color, $switch-bg-color
Navbar / Sidenav $navbar-height (64px / 56px mobile), $sidenav-width (300px)
Components per-component colors and sizes for cards, carousel, collapsible, chips, datepicker, dropdown, toasts, tabs, tables, collections, progress bar

Tip

Because every variable is !default, the safest customization pattern is a thin wrapper SCSS file that sets overrides first and then @import "materialize";. Never edit the framework partials directly — your changes would be lost on upgrade.

4.3 Responsive breakpoints

The breakpoint variables drive both the grid and the responsive helper classes:

Variable Value
$small-screen-up 601px
$medium-screen-up 993px
$large-screen-up 1201px
$small-screen 600px
$medium-screen 992px
$large-screen 1200px

5. The Grunt Build Pipeline

The build is defined in Gruntfile.js. npm scripts (package.json) provide the common entry points:

npm script Command Purpose
dev grunt monitor Watch + live-reload development server
test grunt travis Full CI build + tests
precommit lint-staged Run Prettier on staged JS via Husky pre-commit hook

5.1 Registered Grunt tasks

Task Steps
grunt monitor concurrent:monitor — runs watch + BrowserSync together (the dev loop)
grunt server browserSync + notify — serves the docs at http://localhost:8000
grunt js_compile concat:temp → configureBabel → babel:bin → clean:temp
grunt sass_compile sass:gh → sass:bin → postcss:gh → postcss:bin → notify
grunt jade_compile jade (compile docs templates) → notify
grunt travis js_compile + sass_compile + jasmine (the CI target)
grunt release Full production build (see below)

5.2 The release task

grunt release produces the distributable artifacts:

sass:expanded  -> sass:min       (compile CSS, expanded + minified)
postcss:expanded / postcss:min   (autoprefix both)
concat:dist                      (concatenate JS in fixed order)
babel:dist                       (transpile ES2015 -> ES5)
uglify:dist / uglify:extras      (minify core + extras)
usebanner:release                (prepend license banner)
compress:main / compress:src     (zip standard + source bundles)
compress:starter_template / compress:parallax_template
replace:version / replace:readme (stamp version numbers)
rename:rename_src / rename:rename_compiled
clean:temp

5.3 JavaScript concatenation order

concat assembles the bundle in a fixed order (dependencies first):

js/cash.js          (DOM library)
js/component.js     (base class)
js/global.js        (M namespace)
js/anime.min.js     (animation engine)
js/collapsible.js, dropdown.js, modal.js, materialbox.js, parallax.js,
tabs.js, tooltip.js, waves.js, toasts.js, sidenav.js, scrollspy.js,
autocomplete.js, forms.js, slider.js, cards.js, chips.js, pushpin.js,
buttons.js, datepicker.js, timepicker.js, characterCounter.js,
carousel.js, tapTarget.js, select.js, range.js

Important

cash.js, component.js, and global.js must be concatenated before any component. Components reference cash ($), the Component base class, and the M namespace at definition time. Re-ordering breaks the bundle.

5.4 CSS toolchain

  • Sass compiled by node-sass (via grunt-sass), output in expanded and compressed styles.
  • Autoprefixer (PostCSS) targets: last 2 versions, Chrome >= 30, Firefox >= 30, ie >= 10, Safari >= 8.

5.5 JavaScript toolchain

  • Babel with babel-preset-es2015 (arrow functions, block scoping, classes, template literals, object super) transpiles ES2015 → ES5.
  • UglifyJS minifies the result.
  • Source maps are produced for the bin build.

5.6 Documentation site

The documentation site lives in jade/ (Jade/Pug templates) and is compiled to HTML by grunt-contrib-jade. To run the docs locally:

git clone https://github.com/Dogfalo/materialize
cd materialize
npm install
grunt monitor
Then open http://localhost:8000 — BrowserSync serves the compiled docs and
live-reloads on source changes.

6. Security Analysis

Caution

Materialize at this version has known, unresolved security advisories and is unmaintained. Treat it accordingly in any environment that processes untrusted input.

6.1 Known advisories

Issue #6331 (34 comments) tracks three CVEs that remain open:

Advisory Notes
CVE-2019-11002 Reported against Materialize; unresolved upstream
CVE-2019-11003 Reported against Materialize; unresolved upstream
CVE-2019-11004 Reported against Materialize; unresolved upstream

These relate to cross-site scripting vectors in component handling. Because the project is no longer maintained, no upstream patch is expected.

Warning

Specific exploit details are intentionally not reproduced here. Consult the official CVE database entries and the upstream issue for authoritative guidance before deploying Materialize in security-sensitive contexts.

6.2 Inherent XSS surface

Several components accept HTML strings as options or attributes and inject them into the DOM without sanitization:

  • M.toast({ html: ... }) — toast content.
  • Tooltip html option / data-tooltip attribute.
  • Autocomplete suggestion rendering and Chips tags.

If any of this content can originate from users, sanitize it on your side before passing it to Materialize. The framework does no escaping.

6.3 No server-side surface

Materialize is entirely client-side. It performs no network requests, defines no API endpoints, reads no secrets, and has no authentication logic. Its security profile is limited to DOM/XSS concerns in the browser.

6.4 Mitigations

  • [ ] Sanitize any user-derived content before passing it to html options.
  • [ ] Pin to a specific version and review the CVE advisories for applicability.
  • [ ] Consider a maintained fork or an actively maintained alternative for new long-lived projects.
  • [ ] Apply a Content Security Policy to limit the blast radius of any injection.

7. Code Quality & Conventions

Aspect Detail
Language level ES2015 (classes, arrow functions, template literals), transpiled to ES5 for distribution
DOM access Internal cash library, never a hard jQuery dependency
Formatting Prettier, enforced on staged js/*.js via lint-staged + Husky precommit hook
Linting .eslintrc present at repo root
Editor config .editorconfig, .prettierrc, .prettierignore at root
Component consistency Every component implements the same static surface (init, getInstance, defaults, destroy) — highly uniform, easy to reason about
Instance storage Each instance is stored on its element as el.M_<Name>, enabling getInstance() lookups without a registry
Memory hygiene Components implement destroy() to remove event listeners and DOM additions; the base constructor auto-destroys a prior instance before re-init

Note

The codebase predates ES modules — it relies on concatenation order and a shared global (M) rather than import/export between files. This is the main structural constraint a modernization effort would need to address.


8. Testing

Aspect Detail
Framework Jasmine (grunt-contrib-jasmine)
DOM assertions jasmine-jquery
Browser PhantomJS (phantomjs-prebuilt)
Specs tests/spec/**/*Spec.js
Helper tests/spec/helper.js
Tested under the compiled bin/materialize.js + bin/materialize.css
Run grunt travis (or grunt jasmine directly after a build)

Specs exist for: autocomplete, cards, carousel, chips, collapsible, dropdown, fab, materialbox, modal, select, sidenav, tabs, toast, and tooltip. Note that not all 23 components have spec coverage — date/time pickers, slider, scrollspy, pushpin, parallax, range, and character counter are not covered by Jasmine specs.

There is also a manual test/html/ directory with standalone HTML pages for visually exercising components (badges, buttons, cards, carousel, chips, collapsible, dropdown, fixed_navbar, forms, materialbox, multiple_modals, multiple_sidenav, overlayZindex, pushpin, scrollfire, scrollspy, tabs, waves).


9. Operations: Deploying Materialize

Materialize ships as static assets, so "deployment" means hosting two files (plus the icon font).

Production checklist

  • [ ] Use minified assets: materialize.min.css, materialize.min.js.
  • [ ] Link the Material Icons font (self-host it if you cannot depend on Google Fonts).
  • [ ] Serve assets over HTTPS with long-lived cache headers (they are versioned/immutable per release).
  • [ ] Pin the exact version (CDN URL or package.json version) rather than a floating tag.
  • [ ] Initialize components after DOMContentLoaded (or via a bundler/framework lifecycle hook).
  • [ ] If using a bundler, import from dist/js/materialize.js (the main field) or sass/materialize.scss (the sass field) to compile only what you need.

Bundler integration

The npm package declares module entry points:

{
  "main":  "dist/js/materialize.js",
  "style": "dist/css/materialize.css",
  "sass":  "sass/materialize.scss"
}

With a Sass-aware bundler you can @import the Sass entry (or individual component partials) and override variables, getting tree-trimmed CSS for your component subset.


10. Improvements & Technical Debt

Observations drawn from the source and the community issue tracker:

Area Observation Suggested direction
Maintenance Project is unmaintained (#6615, #6691); open CVEs (#6331) Migrate to a maintained fork/alternative for new projects, or fork and patch
Modularity No way to install/include a single component from the standard build (#6422) The Sass @import exclusion approach partially solves CSS; JS would need an ES-module refactor
Module system Files rely on concat order + global M rather than ES imports Refactor to ES modules with explicit imports; would also enable real tree-shaking
XSS hardening html options inject unsanitized markup Add optional sanitization or document the risk prominently in component APIs
Test coverage ~14 of 23 components have Jasmine specs Add specs for datepicker, timepicker, slider, scrollspy, pushpin, parallax, range, characterCounter
Select reliability Multiple open issues on the custom Select across iOS/frameworks (#6647, #6698, #4529, #1861) Harden custom select or improve browser-default fallback guidance
Build tooling Grunt + PhantomJS are themselves aging/deprecated Modernize to a current bundler and a headless-Chrome test runner
Datepicker bug Wrong date when setDate runs pre-init (#6011) Guard setDate against being called before initialization completes

11. Quick Reference

Global API (M)

Call Effect
M.AutoInit() Initialize all components on the page
M.<Component>.init(els, opts) Initialize one/many elements
M.<Component>.getInstance(el) Retrieve an instance
M.toast({ html }) Show a toast
M.Toast.dismissAll() Dismiss all toasts
M.updateTextFields() Re-float labels after setting input values
M.textareaAutoResize($el) Resize a textarea to fit content
M.validate_field($el) Apply validation styling
M.version Framework version string ('1.0.0')

Build commands

Command Purpose
npm install Install dev dependencies
grunt monitor Dev watch + live reload (docs at localhost:8000)
grunt travis CI build + Jasmine tests
grunt release Full production build (dist + zips)
grunt js_compile Concatenate + Babel-transpile JS only
grunt sass_compile Compile + autoprefix CSS only

Key files

Path Role
sass/materialize.scss Sass entry point (component imports)
sass/components/_variables.scss All design tokens (!default)
js/component.js Base Component class
js/global.js M namespace + auto-init + jQuery wrapper
Gruntfile.js Build pipeline definition
package.json Package metadata, scripts, dev dependencies
dist/ Compiled CSS/JS (release output)
extras/noUiSlider/ Optional standalone range slider