Migrate from @material/web

lit-material reads the same Material 3 system tokens as @material/web, so a theme carries over as it is. A codemod rewrites elements, attributes, slots and imports, and lists by file and line what it left for you.

What stays and what changes

  • The same tokens. Components read the --md-sys-* custom properties (colour roles, typescale, shape, elevation, state), the names a Material Theme Builder export sets. Keep your theme and link @lit-material/tokens/css/index.css once for the baseline. The .md-typescale-* classes are in @lit-material/tokens/css/typography.css under the same names.
  • One package per component. import "@material/web/button/filled-button.js" becomes import "@lit-material/button", which registers every button variant.
  • Variants are attributes. One element per component: <md-outlined-text-field> is <lit-material-text-field variant="outlined">, <md-filled-tonal-button> is <lit-material-button variant="tonal">. Labels that were attributes (label on chips and FABs) are text content.
  • Component tokens differ. Per-component tokens such as --md-filled-button-container-color have no counterparts. Each lit-material element lists its own --lit-material-* properties and CSS parts on its page; colours and shapes come from the system tokens.
  • Forms. Text fields, selects, checkboxes, radios, switches, sliders and buttons are form-associated through ElementInternals: FormData, constraint validation and reset work as with native controls.
  • Server rendering. Every component is tested against @lit-labs/ssr, and @lit-material/vite renders whole apps on the server. See Server rendering.
  • More components. Data table, date and time pickers, navigation drawer, rail and bar, snackbar, tooltip, stepper and the rest of the catalogue, which @material/web doesn't have.

Run the codemod

Commit first: the codemod rewrites files in place. A dry run prints every change and warning and writes nothing:

npx @lit-material/codemod md-web src --dry-run
npx @lit-material/codemod md-web src

It reads HTML (with parse5, a spec-compliant parser), Lit templates in .ts/.js, JSX and TSX, and CSS, and edits the text in place, so formatting, comments and expressions stay as they were. Binding syntax is kept: .prefixText=${p} becomes .prefix=${p}, Angular's [selected] becomes [checked]. It ends with the packages to install:

  changed  src/app.ts:12: <md-outlined-text-field> → <lit-material-text-field variant="outlined">
  changed  src/app.ts:12: <md-outlined-text-field> prefix-text → prefix
  warning  src/app.ts:31: <md-slider> range: lit-material-slider is single-value; a two-thumb range is not supported

Install what the rewritten files use:  npm install @lit-material/tokens @lit-material/button @lit-material/text-field …

Anything without a confident mapping is a warning with its file and line, never a silent change: unknown md-* elements, attributes with no counterpart, bound values that would need mapping, class imports (import { MdDialog }), md-* names in selectors and strings, and component tokens. Running it twice changes nothing the second time. The same rewrite is a function, for your own tooling: import { transform } from "@lit-material/codemod".

Element by element

This table is the codemod's own mapping, so it is what the codemod does. Attributes not listed carry over unchanged.

@material/weblit-materialPackageRewritten
md-filled-buttonlit-material-button variant="filled"@lit-material/buttonhas-icon removed
Reported, left as is: trailing-icon, soft-disabled, download
md-outlined-buttonlit-material-button variant="outlined"@lit-material/buttonhas-icon removed
Reported, left as is: trailing-icon, soft-disabled, download
md-text-buttonlit-material-button variant="text"@lit-material/buttonhas-icon removed
Reported, left as is: trailing-icon, soft-disabled, download
md-elevated-buttonlit-material-button variant="elevated"@lit-material/buttonhas-icon removed
Reported, left as is: trailing-icon, soft-disabled, download
md-filled-tonal-buttonlit-material-button variant="tonal"@lit-material/buttonhas-icon removed
Reported, left as is: trailing-icon, soft-disabled, download
md-icon-buttonlit-material-icon-button@lit-material/icon-buttonslot="selected" → slot="selected-icon", unslotted md-icon → slot="icon"
Reported, left as is: soft-disabled, flip-icon-in-rtl, aria-label-selected
md-filled-icon-buttonlit-material-icon-button variant="filled"@lit-material/icon-buttonslot="selected" → slot="selected-icon", unslotted md-icon → slot="icon"
Reported, left as is: soft-disabled, flip-icon-in-rtl, aria-label-selected
md-filled-tonal-icon-buttonlit-material-icon-button variant="tonal"@lit-material/icon-buttonslot="selected" → slot="selected-icon", unslotted md-icon → slot="icon"
Reported, left as is: soft-disabled, flip-icon-in-rtl, aria-label-selected
md-outlined-icon-buttonlit-material-icon-button variant="outlined"@lit-material/icon-buttonslot="selected" → slot="selected-icon", unslotted md-icon → slot="icon"
Reported, left as is: soft-disabled, flip-icon-in-rtl, aria-label-selected
md-fablit-material-fab@lit-material/fabvariant → color, size: medium → regular, label → text + extended, adds color="surface"
Reported, left as is: lowered
md-branded-fablit-material-fab@lit-material/fabsize: medium → regular, label → text + extended, adds color="surface"
Reported, left as is: lowered
md-checkboxlit-material-checkbox@lit-material/checkboxnone
Reported, left as is: touch-target, input event
md-radiolit-material-radio@lit-material/radionone
Reported, left as is: touch-target, input event
md-switchlit-material-switch@lit-material/switchselected → checked
Reported, left as is: icons, show-only-selected-icon, input event
md-sliderlit-material-slider@lit-material/slidernone
Reported, left as is: range, value-start, value-end, name-start, name-end, aria-label-start, aria-label-end, value-label, value-label-start, value-label-end, labeled, ticks
md-filled-text-fieldlit-material-text-field variant="filled"@lit-material/text-fieldtype="textarea" → lit-material-textarea, prefix-text → prefix, suffix-text → suffix, input-mode → inputmode, has-leading-icon removed, has-trailing-icon removed
Reported, left as is: no-spinner, text-direction, cols, multiple, select event
md-outlined-text-fieldlit-material-text-field variant="outlined"@lit-material/text-fieldtype="textarea" → lit-material-textarea, prefix-text → prefix, suffix-text → suffix, input-mode → inputmode, has-leading-icon removed, has-trailing-icon removed
Reported, left as is: no-spinner, text-direction, cols, multiple, select event
md-filled-selectlit-material-select variant="filled"@lit-material/selectquick removed
Reported, left as is: selected-index, display-text, has-leading-icon, menu-positioning, clamp-menu-width, menu-align, typeahead-delay, input event, opening event, opened event, closing event, closed event, slot="leading-icon"
md-outlined-selectlit-material-select variant="outlined"@lit-material/selectquick removed
Reported, left as is: selected-index, display-text, has-leading-icon, menu-positioning, clamp-menu-width, menu-align, typeahead-delay, input event, opening event, opened event, closing event, closed event, slot="leading-icon"
md-select-optionlit-material-select-option@lit-material/selectslot="headline" → default slot, slot="start" → slot="leading"
Reported, left as is: display-text, slot="end", slot="supporting-text", slot="trailing-supporting-text", slot="overline"
md-chip-setnonelit-material has no chip set: put the chips in a plain container (a flex row with a gap); arrow-key movement between chips isn't provided
md-assist-chiplit-material-chip variant="assist"@lit-material/chiplabel → text, slot="icon" → slot="leading-icon"
Reported, left as is: always-focusable
md-filter-chiplit-material-chip variant="filter"@lit-material/chiplabel → text, slot="icon" → slot="leading-icon"
Reported, left as is: always-focusable, has-selected-icon
md-input-chiplit-material-chip variant="input"@lit-material/chiplabel → text, adds removable, slot="icon" → slot="leading-icon"
Reported, left as is: always-focusable, avatar, remove-only
md-suggestion-chiplit-material-chip variant="suggestion"@lit-material/chiplabel → text, slot="icon" → slot="leading-icon"
Reported, left as is: always-focusable
md-dialoglit-material-dialog@lit-material/dialogquick removed, closed event → close, slot="content" → default slot
Reported, left as is: no-focus-trap, type, return-value, open event, opened event
md-menulit-material-menu@lit-material/menuquick removed, closed event → close
Reported, left as is: positioning, has-overflow, x-offset, y-offset, anchor-corner, menu-corner, typeahead-delay, stay-open-on-outside-click, stay-open-on-focusout, skip-restore-focus, default-focus, is-submenu, opening event, opened event, closing event, close-menu event
md-menu-itemlit-material-list-item interactive@lit-material/listtype removed, slot="headline" → default slot, slot="start" → slot="leading", slot="end" → slot="trailing", slot="trailing-supporting-text" → slot="trailing"
Reported, left as is: keep-open
md-sub-menunonelit-material-menu has no submenus: flatten the menu, or open a second lit-material-menu anchored to the item
md-listlit-material-list@lit-material/listnone
md-list-itemlit-material-list-item@lit-material/listtype="text" → removed, type="link" → removed, type="button" → interactive, slot="headline" → default slot, slot="start" → slot="leading", slot="end" → slot="trailing", slot="trailing-supporting-text" → slot="trailing"
md-tabslit-material-tabs@lit-material/tabsactive-tab-index → selected, auto-activate removed
md-primary-tablit-material-tab@lit-material/tabshas-icon removed
Reported, left as is: active, icon-only, inline-icon
md-secondary-tablit-material-tab@lit-material/tabshas-icon removed
Reported, left as is: active, icon-only
md-linear-progresslit-material-linear-progress@lit-material/progressnone
Reported, left as is: buffer, four-color
md-circular-progresslit-material-circular-progress@lit-material/progressnone
Reported, left as is: four-color
md-dividerlit-material-divider@lit-material/dividerinset → inset-start inset-end
md-iconlit-material-icon@lit-material/iconnone
md-ripplenonelit-material components draw their own ripple, focus ring and elevation (style them with ::part(ripple), ::part(focus-ring) or the component's tokens); remove this element
md-focus-ringnonelit-material components draw their own ripple, focus ring and elevation (style them with ::part(ripple), ::part(focus-ring) or the component's tokens); remove this element
md-elevationnonelit-material components draw their own ripple, focus ring and elevation (style them with ::part(ripple), ::part(focus-ring) or the component's tokens); remove this element

Imports follow the elements: @material/web/<folder>/<file>.js becomes the element's package, once per file. @material/web/all.js is reported with the imports to use instead, and left for you to replace.

What needs a person

  • Icons. <md-icon>settings</md-icon> draws a Material Symbols ligature from an icon font. lit-material-icon loads no font, so the word shows as text: register the SVGs with registerIcons() and use name, or slot an <svg>. The codemod reports each ligature.
  • Script code. The codemod changes markup and imports. Code that reads switch.selected (now checked), tabs.activeTabIndex (now selected), queries md-* selectors, or imports md-web classes is reported, not rewritten.
  • Labels. A <label for> pointing at lit-material-checkbox, -radio or -switch doesn't toggle it (checked in Chromium); wrapping the control in the <label> does. Prefer the element's label attribute, which also names it for assistive technology.
  • Component tokens. Each --md-<component>-* token is reported with the lit-material element's own properties and parts to use instead.
  • Missing pieces. No chip set, submenus, two-thumb slider, linear progress buffer, secondary tab style, or standalone ripple, focus ring and elevation elements (components draw their own). The table lists every attribute and event left in place.