# Materialize Admin & Developer Guide

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](Dogfalo__materialize__UserGuide.md), 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](#6-security-analysis).

---

## 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.

```text
+-----------------------------------------------------------+
|                     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`:**

```javascript
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:

```javascript
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:

```javascript
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:

```scss
@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:

```scss
// 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:

```text
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):

```text
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:

```bash
git clone https://github.com/Dogfalo/materialize
cd materialize
npm install
grunt monitor
```

```text
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:

```json
{
  "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 |

---

_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. Unofficial; not affiliated with or reviewed by the project._
