Foundation

Icons

A consistent icon system that provides visual clarity and reinforces meaning across the interface. Users building with the AI agent can choose their icon library; Phosphor is the default.

Usage

Overview

Marmo UI lets you build with any of several popular MIT-licensed icon libraries. The AI agent asks which library you want to use before generating UI code, and the marmo-ui MCP enforces your choice — review_generated_code rejects icons imported from any library other than the one you selected.

The default icon library is Phosphor Icons (@phosphor-icons/react, MIT). Phosphor ships outline, duotone, and filled variants from a single set via a weight prop, which maps cleanly to the three icon styles the product uses.

  • Icons are rendered as inline SVGs for crisp rendering at any size.
  • Icons inherit the current text color by default, making them easy to theme.
  • The Icon wrapper component (@marmoui/ui) provides consistent sizing, alignment, and two-tone color tokens for any library you pick.

Choosing an icon library

When you build UI with the AI agent, it asks which library you want. Pass the library id to get_design_guidelines and review_generated_code so the rules and validator match.

LibraryidPackageLicenseWeights
Phosphor Icons (default)phosphor@phosphor-icons/reactMITregular · duotone · fill
Material Designmaterialreact-icons/mdMIT(by name)
Lucidelucidelucide-reactISC¹outline only
Tablertabler@tabler/icons-reactMIToutline + *Filled
Heroiconsheroicons@heroicons/reactMIToutline · solid
Featherfeatherreact-icons/fiMIToutline only

¹ Lucide is ISC-licensed, not MIT. It is included in the picker for convenience, but if your policy requires strict MIT-only, choose one of the other libraries.

The three product icon styles

The product uses three icon styles. Each library maps to them as follows:

StylePhosphorMaterialHeroiconsLucide / Tabler / Feather
outlineweight="regular" (default)Md… filled set, or MdOutline…@heroicons/react/24/outlinedefault
duotoneweight="duotone"(no native duotone)(no native duotone)(no native duotone)
filledweight="fill"Md… filled set@heroicons/react/24/solidTabler *Filled; Lucide/Feather n/a

Phosphor is the only supported library with a native duotone weight. If your designs rely on duotone icons, keep the default (phosphor).

Phosphor (default)

import { Plus, PencilSimple, Trash, MagnifyingGlass } from '@phosphor-icons/react';

// outline (default)
<Plus />

// duotone — two-tone icons; the Icon wrapper maps the secondary color to --color-<fillSecondary>
<Plus weight="duotone" />

// filled
<Plus weight="fill" />

Common icon mappings (Phosphor):

ActionIcon
Add / CreatePlus
EditPencilSimple
DeleteTrash
SearchMagnifyingGlass
CloseX
CheckCheck
Arrow rightArrowRight
SettingsGearSix
UserUser
Download / UploadDownloadSimple / UploadSimple
More optionsDotsThreeVertical / DotsThree
RefreshArrowsClockwise
Info / Warning / ErrorInfo / Warning / XCircle

Product icon weight policy

The library's built-in icons follow a consistent weight convention so dashboards, navigation, and chrome each read distinctly. Apply the same convention in your own UI:

SurfaceWeightExample
Most icons (chrome, actions, form controls)regular (default — omit weight)<GearSix />, <Plus />
Dashboard / analytics / chart tilesduotone<SquaresFour weight="duotone" />, <ChartDonut weight="duotone" />
Primary navigation iconsregular<SquaresFour />
Secondary navigation — default stateregular<AddressBook />
Secondary navigation — active / hoverfill<AddressBook weight="fill" />
Solid status glyphs (check, star, circle, avatar)fill<Check weight="fill" />, <Star weight="fill" />
Dropdown / select caretregular<CaretDown />
Menu toggleregular<List />
import { SquaresFour, CaretDown, List, AddressBook, Check } from '@phosphor-icons/react';

// Dashboard tile — duotone
<SquaresFour weight="duotone" />

// Dropdown caret + menu toggle — regular (no weight)
<CaretDown />
<List />

// Secondary nav: regular default, filled when active
<AddressBook />              // default
<AddressBook weight="fill" /> // active

Brand marks (kept)

The only custom SVGs in the library are the Marmo brand marks, exported from @marmoui/ui/icons. They are brand assets, not icon-library icons, and are independent of your chosen icon library:

MarkNameUsage
Marmo markMarmoIconSquare brand glyph
Marmo wordmarkMarmoLogoWordmarkMark + "Marmo" wordmark
Step indicatorStepIcon / StepFilledIconStepper completed/active step glyphs

Best Practices

  • Pick one icon library per project and stick with it. The validator rejects mixed-library imports.
  • Use icons consistently across similar actions and contexts. The icon map in get_design_guidelines({ iconLibrary }) gives you the canonical name per action for each library.
  • Pair icons with text labels when the icon meaning may be ambiguous.
  • Use the Icon wrapper component for consistent sizing, alignment, and two-tone color support (works with any library).
  • Maintain a 1:1 aspect ratio for all icons.
  • Use semantic color tokens for icon colors (e.g., text-icon, text-icon-primary).

When Not to Use

  • Avoid decorative icons that add no meaning or context.
  • Don't use icons as the sole means of conveying critical information without accessible text alternatives.
  • Don't mix icon libraries inconsistently within the same project.

Sizing

Icons follow a consistent sizing scale that aligns with the component size system. The Icon wrapper applies these regardless of library:

SizeDimensionsUse Case
xs12pxCompact elements, inline indicators
sm16pxSmall buttons, form fields, table cells
md20pxDefault size, most UI contexts
lg24pxProminent actions, headers
xl32pxFeature highlights, empty states

Color

Icons use semantic color tokens to ensure proper contrast and meaning:

TokenCSS VariableUsage
Default--color-iconStandard icon color
Light--color-icon-lightSubtle, secondary icons
Primary--color-icon-primaryPrimary action icons
Destructive--color-icon-destructiveDestructive action icons

For duotone icons (Phosphor weight="duotone"), the Icon wrapper's fill prop sets the primary layer and fillSecondary sets the secondary layer.

Accessibility

  • Always provide aria-label or aria-hidden="true" on icon elements.
  • Icons used as interactive elements (icon buttons) must have accessible names.
  • Decorative icons should be hidden from screen readers with aria-hidden="true".
  • Ensure icon color contrast meets WCAG 2.1 AA standards (3:1 ratio for non-text elements).

AI agent workflow

When generating UI with the marmo-ui MCP:

  1. The agent asks which icon library you want (default: phosphor).
  2. It calls get_design_guidelines({ iconLibrary: '<your choice>' }) to load that library's import source, icon name map, and weight mapping.
  3. It imports icons only from your chosen library.
  4. It calls review_generated_code({ code, iconLibrary: '<your choice>' }) before returning the code. Imports from any other icon library are reported as errors and must be fixed before the code is returned.