Skip to content

Material 3 → Verge

A practical, opinionated guide for moving an interface from Google's Material 3 to Verge, the canonical visual language of the Koder Design System. Token rename, component swap, and the few philosophical differences worth knowing. Verge v0 is Adwaita-based, not Material-based — most M3 tokens have a Verge counterpart but the visual language differs.

Why migrate

  • Self-hosted — Koder Design ships from your own infrastructure — no Google CDN, no third-party fetch, no version pinning on someone else's release cycle.
  • Full theme control — Light and dark are the baseline; 35 preset themes ship out of the box, and you can override every token at three customisation levels (L0 brand-only → L4 token-level).
  • 35 presets — Era, Brand, Mood, Cultural, Domain families — pick or compose. Material You is one personalisation strategy; Koder treats it as one variant of many.
  • Open spec — Every page on this site is generated from a spec under `meta/docs/stack/specs/`. Fork the spec, regenerate the design system — no black box.
  • No tracking — Koder Design's docs site uses no analytics scripts, no per-page beacon. The reporter widget at the bottom is opt-in and anonymous.

Token mapping

Most Material 3 colour, shape, motion, and typography tokens have a 1:1 Koder counterpart. Where the philosophy differs, the table calls out the rationale.

Material 3 tokenKoder Design tokenNotes
md.sys.color.primary--kdr-accent1:1 rename
md.sys.color.on-primary--kdr-accent-on1:1 rename
md.sys.color.primary-container--kdr-surface-2Koder collapses Material's container hierarchy to two tiers (`surface` / `surface-2`)
md.sys.color.primary-fixed--kdr-accentKoder doesn't model the fixed-tone variants — use the base role
md.sys.color.surface--kdr-surface1:1 rename
md.sys.color.surface-container--kdr-surface-2Koder collapses Material's container hierarchy to two tiers (`surface` / `surface-2`)
md.sys.color.on-surface--kdr-text1:1 rename
md.sys.color.on-surface-variant--kdr-text-muted1:1 rename
md.sys.color.outline--kdr-border1:1 rename
md.sys.color.error--kdr-error1:1 rename
md.sys.elevation.level0..5--kdr-shadow-0..5Tonal recipe differs; see `specs/themes/elevation.kmd`
md.sys.shape.corner.small--kdr-radius-smKoder ships sm/md/lg only; Material's 5-tier scale collapses
md.sys.shape.corner.medium--kdr-radius-mdKoder ships sm/md/lg only; Material's 5-tier scale collapses
md.sys.shape.corner.large--kdr-radius-lgKoder ships sm/md/lg only; Material's 5-tier scale collapses
md.sys.typescale.display-largevar(--kds-font-display)Koder uses 4 role-based families (sans/mono/display/serif); Material's per-class scale maps via role
md.sys.typescale.body-largevar(--kds-font-sans)Koder uses 4 role-based families (sans/mono/display/serif); Material's per-class scale maps via role
md.sys.motion.easing.standardcubic-bezier(0.2, 0.8, 0.2, 1)Koder ships canonical curves inline (no `--kdr-motion-*` vars yet — tracked for v2)
md.sys.motion.duration.medium2240msKoder ships canonical curves inline (no `--kdr-motion-*` vars yet — tracked for v2)
md.sys.state.hover.opacityn/aKoder uses background-color shifts (surface-2, accent-mix) instead of Material's stateful overlay opacities

Component mapping

Each Koder component is one spec that consolidates the Material variants under a single API (Material agrupa = nós agrupamos).

Material 3 componentKoder Design componentSpec
MaterialButtonKoderButtoncomponents/buttons
FilledButton / TonalButton / OutlinedButton / TextButton / FABKoderButton via `variant=` propcomponents/buttons
Card (3 variants)KoderCardcomponents/cards
CheckboxKoderCheckboxcomponents/checkbox
Chip (4 variants)KoderChipcomponents/chips
Dialog / AlertDialogKoderDialogcomponents/dialogs
Menu / DropdownMenuKoderMenucomponents/menus
NavigationBar / NavigationRail / NavigationDrawerKoderNav via `variant=` propcomponents/navigation
SnackbarKoderSnackbarcomponents/snackbars
SwitchKoderSwitchcomponents/switch
Tabs (primary / secondary)KoderTabscomponents/tabs
TextField (filled / outlined)KoderTextFieldcomponents/text-fields
Tooltip (plain / rich)KoderTooltipcomponents/tooltips
DatePicker / TimePickerKoderDatePicker / KoderTimePickercomponents/pickers
BottomSheet / SideSheetKoderSheetcomponents/sheets

Breaking differences

  1. 13-tone palette → 18-role taxonomy. Material derives 13 tones per key colour and exposes them as roles. Koder skips the intermediate tone step — its 18 semantic roles are mapped to HCT-derived seed colours directly. Practical effect: when you need a colour, ask for the role (`accent-on`, `surface-2`), not the tone.
  2. Material You → presets + seed-color. Material You re-derives the system from the user's wallpaper on Android. Koder Design treats this as one of 35+ preset strategies and offers an explicit seed-color customisation level (L2). Cross-platform parity matters more than wallpaper extraction.
  3. Tonal elevation only at dark. Material 3 uses tonal elevation in both schemes. Koder uses shadow elevation in light mode and tonal overlay in dark mode — combined where contrast demands it. See `specs/themes/elevation.kmd`.
  4. State overlay opacity → background swap. Material applies a translucent overlay (`hover.opacity = 0.08`, etc.) on top of the base colour. Koder swaps to a different surface token at the same role. Cleaner cascade, simpler theming, less translucency stack to debug.
  5. Single sans default, opt-in serif. Material 3 ships Roboto Flex as a variable font by default. Koder ships Inter (sans) + JetBrains Mono (mono) — self-hosted WOFF2. Serif is opt-in for content-heavy surfaces (e.g., reading mode); display fonts are commissioned per preset.

Stepwise upgrade

  1. Audit your current token surface.

    Run `grep -r 'md.sys' src/` to enumerate every Material token you reference. Map each to its Koder equivalent using the table above. Leave the rest at default for the first pass — opinionated migrations get into trouble when you try to translate everything at once.

  2. Swap the theme provider.

    Replace your `MaterialApp` / `MaterialTheme` wrapper with Koder Design's `KoderApp` (Flutter) or `<koder-design>` (Web). Either accepts a `preset=` prop (default: `koder-base`) plus optional `seedColor` for L2 customisation.

  3. Rename component imports.

    Use the component table above. Most renames are mechanical (`MaterialButton` → `KoderButton`); a few collapse multiple Material widgets into a single Koder one via a `variant=` prop (button styles, nav surfaces, dialog types).

  4. Tune the L2 seed-color.

    Open `/playground/` and paste your brand seed. The 18-role palette derives automatically. If you need finer control (L3 / L4), edit your token override file directly — the playground emits the JSON for you.

  5. Verify with the contrast checker.

    Pop /tools/contrast/ to spot-check each token pair against WCAG 2.2 AA / AAA. Pay special attention to the `text-muted` × `surface-2` pair — Material's tone scale was permissive here; Koder's defaults are tighter but worth verifying when you bring in a brand colour.

Known gaps

These are Material features Koder Design does not yet match. Tracked publicly:

  • Wallpaper-derived dynamic colour (Material You on Android). Koder offers seed-color customisation but not OS-level wallpaper extraction. Tracked: `themes/color-dynamic.kmd` v2.
  • Variable-axis font with grade + optical-size axes (Material Symbols, Roboto Flex). Koder ships Inter + JetBrains Mono — single weight axis. Koder Display custom commission tracked in `projects/koder-stack#128`.
  • Figma kit + plugin parity. Spec exists (`specs/tools/design-kit-export.kmd`); implementation is the slowest of the 5 tool tracks.
  • Per-platform code samples on every component page (Flutter / Web / Android Compose / iOS SwiftUI). Spec at `specs/develop/code-samples-toggle.kmd`.

Watch progress on the Koder Design release blog →