Skip to content

Releases

30.2.0

Choose a tag to compare

@simeonoff simeonoff released this 10 Nov 09:20
a8c9eed

A feature release. It adds a tile manager theme, a public theme-markers() mixin for declaring the active theme, and theme parameters for tokens the input group and combo schemas already had. The MCP server now resolves components by their selectors and React/Blazor names, and its component theme tool tells agents how to cover both the light and dark variants.

Themes

Tile manager

A new tile-manager-theme() function styles the tile manager and its tiles:

$my-tile-manager-theme: tile-manager-theme(
  $background: #eee,
  $tile-background: white,
);

@include tokens($my-tile-manager-theme);

$tile-background is the primary token. When it's set, the header and content backgrounds follow it, and the title and content text colors are derived from those backgrounds with adaptive-contrast(). The theme also takes the border, hover border, divider, drag-and-drop placeholder, overlay and ghost colors, the border radius, and the resting and drag shadows. The shadows default to the schema's resting-elevation and drag-elevation.

The Tailwind utilities include a tile-manager utility for tile-background. (#667)

Theme markers

The new theme-markers($theme, $variant) mixin declares --ig-theme and --ig-theme-variant in the current scope. The markers inherit, so include the mixin where the active theme is declared, such as :root, or on an element whose subtree uses another theme:

:root {
  @include theme-markers(material, light);
}

// Output
:root {
  --ig-theme: material;
  --ig-theme-variant: light;
}

null and values that aren't strings are skipped. An unquoted indigo, which Sass reads as a color keyword, is accepted. tokens($mode: 'scoped') and the Tailwind component utilities emit the markers through this mixin, so their compiled output is unchanged. (#668)

Input group

  • input-group-theme() takes a $filled-secondary-color parameter, the label color in the filled state. When it isn't passed and $idle-secondary-color is, it follows $idle-secondary-color.
  • The schemas now declare filled-secondary-color for every theme, so the filled label matches the idle label by default:
    • Material and Bootstrap light: gray 700, was gray 600
    • Fluent light: gray 900
    • Indigo light: gray 600
    • Fluent dark: gray 800
    • Indigo dark: gray 50 contrast at 60%

Combo

combo-theme() takes $case-icon-color and $case-icon-color-active, the colors of the case-sensitive icon in the search input. The schema already had these tokens, but the theme couldn't override them.

Color picker

The unused swatch-preview-border-radius token is removed from the color picker schema. No component read it, but --swatch-preview-border-radius is no longer in the generated output.

Documentation

SassDoc now marks the theme parameters that only Ignite UI for Angular uses:

  • chip ghost colors and shadow
  • expansion panel $expanded-margin
  • input success and warning colors, and the success shadow
  • tabs ripple colors
  • tree $drop-area-color

It also documents the rating $value-label and $disabled-value-label parameters and fixes the combo-theme() example. (#659)

MCP server

  • Component names resolve from selectors. The component theme, component tokens and layout tools accept a component's key, its Angular or Web Components selector, or its React or Blazor name (nav-drawer, igc-nav-drawer, IgrNavDrawer). The metadata now lists React and Blazor names for all components, and search matches them.
  • Light and dark guidance. create_component_theme explains that each call produces one variant and that the dark variant usually needs different token values. Each response ends with a note on generating the other variant, with an example of the theme's selector scoped under a dark-mode selector. The note says to put that selector on html or body so overlay content is included. (#660)
  • Tile manager. Adds metadata for the tile manager and its tiles: selectors for every platform, aliases (tiles, dashboard tiles), and resting-shadow/drag-shadow token aliases. (#667)

30.1.0

Choose a tag to compare

@simeonoff simeonoff released this 03 Nov 07:57
e223354

A fix release. It changes how scoped component tokens apply when included inside another selector, makes the spacing and sizing registrations work inside selectors, lets runtime surface overrides reach components in legacy palettes, and makes $scales family maps and the fit_color_scale MCP tool work as documented. It also warns when a deprecated Sass API is used.

Themes

Scoped tokens inside another selector

tokens($theme, $mode: 'scoped') included inside a selector no longer declares the component's tokens on that selector. The tokens go on the component inside it, and on the selector itself only when it is the component:

.promo {
  @include tokens(tooltip-theme($background: red), $mode: 'scoped', $scope: '.igx-tooltip');
}

// 30.0.0
.promo, .promo .igx-tooltip { --background: red; … }

// 30.1.0
.promo .igx-tooltip, .promo:where(.igx-tooltip) { --background: red; … }

Before, every element inside .promo inherited the tooltip's generic tokens (--background, --foreground, …), so for example every grid toolbar in the section turned red. A class placed on the component itself (<igx-avatar class="vip">) still gets the tokens. :where() keeps its specificity unchanged, so a later .vip { --background: … } rule still wins. A theme map without a selector and with no $scope still targets the parent (&). (IgniteUI/igniteui-angular#17901)

To restyle a component for a whole section, use the default global mode. .promo { @include tokens(tooltip-theme(…)); } declares only --ig-tooltip-* tokens, which no other component reads.

Derived grid inputs

The grid, grid toolbar, paginator, column actions, Excel-style filtering and query builder schemas now declare the background, foreground and accent-color inputs their theme functions accept, with the value initial. These components no longer pick up a generic --background, --foreground or --accent-color declared on an ancestor or on :root.

Spacing and sizing

  • spacing() and sizing() emit their @property rules (--ig-spacing, --ig-size-small|medium|large) at the stylesheet root. When these mixins were included inside a selector or a media query, the rules were nested and the browser dropped them. (#652)
  • The pad() function and the sizable() mixin fall back to the initial values (--ig-spacing: 1, --ig-size-medium: 2) when the properties aren't registered.

Tabs

When $tab-ripple-color isn't passed, tabs-theme() now derives it from $item-active-background / $item-background with adaptive-contrast() instead of text-contrast(). Before, when the background was a CSS variable, text-contrast() returned the background itself, so the ripple was the same color as the tab.

Palettes

  • Legacy surfaces follow --ig-surface-500 at runtime. In palettes built with the legacy generator, which includes all the built-in palettes, --ig-surface-base is now var(--ig-surface-500) instead of a copy of its value. Overriding --ig-surface-500 at runtime reaches components again. The compiled colors are the same. Fitted palettes are unchanged; there, --ig-surface-500 already points to --ig-surface-base.
  • $scales maps keyed by family now reach palette(). (#634)
    • A top-level (anchor: …) is read as one spec for every family. Each family keeps its default range and curve.
    • A spec without a range takes the family's default range, and also its default curve unless the spec sets one. A range without a curve is still a straight line.
    • An unquoted white is accepted as an anchor. A color such as #fff is still rejected, with an error that says to quote the keyword.
    • palette() warns about $scales keys it ignores: unknown family names, a surface key, and extra keys next to range/curve/anchor in a single spec.
    • palette() warns once that the legacy generator ignores $scales.

Deprecations

The following APIs now print a deprecation warning, once per compilation:

API Use instead
css-vars($theme) tokens($theme)
text-contrast() adaptive-contrast()
to-opaque() CSS color-mix()
to-hsl() Nothing. It will be removed in a future release.

The library itself no longer calls any of them, and all the SassDoc examples use tokens().

MCP server

  • fit_color_scale takes a family parameter that sets the key of the returned $scales map. name is now only a label. If family is omitted and name is a family name, in any case, it still sets the key. The tool explains the gray anchor and links the scales guidance by its theming:// URI.
  • Scales schema: family maps are no longer reduced to an empty spec, so per-family scales reach the generated Sass. Unknown family keys, a surface key, and single specs mixed with family keys are rejected, each with its own message at the right path.
  • Web Components theme template now matches the library (#639):
    • The progress counters are registered as <number>, so fractional progress labels work.
    • Sizes are registered with sizing() instead of being set as plain variables.
    • Adds the tile manager view transitions and the .ig-scrollbar utility class. The scrollbar variables are emitted for custom palettes too, and the Indigo track uses gray 200.
    • The @property rules, tile manager transitions and .ig-scrollbar sit outside the generated theme() mixin, so including that mixin under a selector doesn't scope them.
    • The React and Blazor examples are generated from the same template. This fixes the dark examples, which referenced undefined variables.

Dependencies

  • zod is upgraded from 3 to 4 (runtime dependency). In the generated TypeScript types for the JSON palettes, shade maps are now Partial<Record<Shade, string>>. Shade maps never required every key, and the type now says so.
  • happy-dom 21 (preview, dev only), actions/setup-node and transitive security updates (body-parser, fast-uri, qs).

30.0.0

Choose a tag to compare

@simeonoff simeonoff released this 22 Oct 10:21
2b96f3a

This release is about color. The palette() function has a new shade generator that builds a usable, accessible scale from any brand color, including very dark or very light ones that the old generator couldn't handle. Surfaces get named layers instead of numbered shades.

Before you upgrade

If you only use the built-in palettes (Material, Fluent, Bootstrap, Indigo, light or dark), nothing changes visually. They still use the original generator and render the same colors as 29.x. The one thing to check is runtime overrides of --ig-surface-500 (see "Surfaces" below).

If you call palette() with your own colors, your shades will change. That's the main point of the release, but there are three things to know:

  • 500 is no longer your exact brand color. Shades are now placed by contrast rather than pinned to the seed, so palette($primary: #0099ff, …) gives --ig-primary-500: rgb(9, 109, 183). The original color is still available as --ig-primary-seed, and seed-lands-on($palette, 'primary') tells you which shade is closest to it ('300' in this example).

  • Overriding --ig-primary-500 at runtime no longer recolors the whole family. Fitted shades are emitted as plain values instead of being derived from 500 with hsl(from var(--ig-primary-500) …). To rebrand at runtime, generate the new palette and swap it in.

  • To keep your 29.x colors exactly while you migrate, opt out per palette or globally. Either way the output is identical to 29.x:

    // One palette
    $p: palette(#0099ff, #df1b74, #fff, $generator: 'legacy');
    
    // Everything
    @use 'igniteui-theming' with ($shade-generator: 'legacy');

New shade generator

The old generator multiplied the seed's lightness by fixed values, so a dark seed produced ten dark shades and a pale seed produced several pure whites. The new "fitted" generator separates a color's character (hue and colorfulness, from the seed) from the shape of its scale (how far shades spread, from a contrast target). For example:

Seed 29.x 30.0
Dark blue hsl(210 80% 10%) 50 is #012d5a, so every shade is dark 50 is #e4ecf8, 900 is #0a1623
Pale blue hsl(210 80% 90%) 50 through 300 are all white 50 is #e4edf7, 900 is #0b1621

The generator works in OKLCH and keeps every shade inside sRGB. Seeds that are almost gray produce neutral families instead of picking up a random hue, and muted brand colors keep their tint.

Predictable contrast. With the default scale, any two shades five steps apart meet WCAG AA (4.5:1) against each other: 50/500, 100/600, 200/700, 300/800, 400/900. So a pairing like background: var(--ig-primary-100); color: var(--ig-primary-600) stays readable whatever the brand color is. This applies to the numbered shades of the chromatic families (primary, secondary, info, success, warn, error).

The gray family is the exception. It keeps the library's original rhythm (material), so existing component styling doesn't shift. Only 3 of those 5 pairings meet AA on white; use $scales: ('gray': 'even') if you want all five.

Custom scales

palette() has a new $scales argument for choosing how shades are distributed:

// A preset for one family
$p: palette(#0099ff, #df1b74, #fff, $scales: ('gray': 'carbon'));

// Your own: a contrast range from 50 to 900, plus an optional cubic-bezier curve
$p: palette(#0099ff, #df1b74, #fff, $scales: (
  'primary': (range: 1.1 14, curve: 0.5 0 0.8 0.8)
));
  • Presets: even (the default), material (the original grayscale), tailwind (fitted to Tailwind v4 slate) and carbon (fitted to IBM Carbon gray). They copy those systems' rhythm, not their colors.
  • A custom range or curve can break the five-step AA rule. That's expected; check your pairings.
  • Gray takes an anchor. 'surface' (the default for every scale except material) measures contrast from your background, so in dark themes gray-50 stays close to the page and higher numbers get lighter. 'white' measures from white, which keeps the same ten grays in every theme. Quote the value: unquoted white is a color, and Sass will reject it.
  • If you ask for more contrast than your background allows (say, a 21:1 gray on a dark gray page), you get the closest reachable shade and a compile warning that tells you how far short it fell.

Surfaces

The surface family now has named layers instead of ten numbered shades:

Token What it's for
--ig-surface-base The page background (your surface color)
--ig-surface-sunken Recessed areas
--ig-surface-raised Cards and panels above the page
--ig-surface-overlay Menus, dialogs, popovers
--ig-surface-container A translucent tint layered on top (3% black on light, 3% white on dark)

Each layer has a matching -contrast token. A layer that has no room to move resolves to base. On pure white, for example, raised and overlay are also white, so let borders or shadows do the separating.

Components now read --ig-surface-base instead of --ig-surface-500. In the built-in palettes the two hold the same color, so nothing changes visually. If you override the surface at runtime, for example with --ig-surface-500: #fafafa, components will no longer pick it up. Set --ig-surface-base instead.

Numbered surface shades are deprecated. --ig-surface-50 through --ig-surface-900 are still emitted, and in fitted palettes --ig-surface-500 points to --ig-surface-base. They'll be removed in a future major, so move your own styles to the roles.

Smaller API changes

  • color() and contrast-color() default to base for the surface: color($color: 'surface') returns var(--ig-surface-base). Other families still default to 500.
  • $IPaletteColors now lists the surface roles instead of the numbered shades. If you loop over it and need every key a surface emits, use the new $ISurfaceShades.
  • New public lists: $INumericShades, $IAccentShades, $ISurfaceRoles, $ISurfaceNumericShades, $ISurfaceShades.
  • New configurable variables: $shade-generator, $scales, $family-scales, $surface-roles, $surface-container-alpha, $saturation-floor.
  • The Tailwind theme exposes both the surface roles and the numbered surface shades.
  • contrast() and luminance() are unchanged and still available.

MCP server

  • create_palette and create_theme use the new generator by default and accept scales and generator.
  • New fit_color_scale tool: give it a color ladder from another system, a brand ramp or a screenshot (lightest first), and it returns a scales spec that reproduces that rhythm with your own colors.
  • New guidance/colors/scales resource that explains the presets and their trade-offs.
  • create_custom_palette with explicit surface colors now takes the roles (base, sunken, raised, overlay, container) instead of 50–900.
  • Removed the "this seed is too light or dark for a good palette" warnings, since the new generator handles those seeds.

Full Changelog: v29.1.1...v30.0.0

30.0.0 Beta 2

30.0.0 Beta 2 Pre-release
Pre-release

Choose a tag to compare

@simeonoff simeonoff released this 21 Oct 10:55
26475bf

This release is about color. palette() has a new shade generator that builds a usable, accessible scale from any brand color, including very dark or very light ones that the old generator couldn't handle. Surfaces get named layers instead of numbered shades.

Before you upgrade

If you only use the built-in palettes (Material, Fluent, Bootstrap, Indigo, light or dark), nothing changes visually. They still use the original generator and render the same colors as 29.x. The one thing to check is runtime overrides of --ig-surface-500 (see "Surfaces" below).

If you call palette() with your own colors, your shades will change. That's the main point of the release, but there are three things to know:

  • 500 is no longer your exact brand color. Shades are now placed by contrast rather than pinned to the seed, so palette($primary: #0099ff, …) gives --ig-primary-500: rgb(9, 109, 183). The original color is still available as --ig-primary-seed, and seed-lands-on($palette, 'primary') tells you which shade is closest to it ('300' in this example).

  • Overriding --ig-primary-500 at runtime no longer recolors the whole family. Fitted shades are emitted as plain values instead of being derived from 500 with hsl(from var(--ig-primary-500) …). To rebrand at runtime, generate the new palette and swap it in.

  • To keep your 29.x colors exactly while you migrate, opt out per palette or globally. Either way the output is identical to 29.x:

    // One palette
    $p: palette(#0099ff, #df1b74, #fff, $generator: 'legacy');
    
    // Everything
    @use 'igniteui-theming' with ($shade-generator: 'legacy');

New shade generator

The old generator multiplied the seed's lightness by fixed values, so a dark seed produced ten dark shades and a pale seed produced several pure whites. The new "fitted" generator separates a color's character (hue and colorfulness, from the seed) from the shape of its scale (how far shades spread, from a contrast target). For example:

Seed 29.x 30.0
Dark blue hsl(210 80% 10%) 50 is #012d5a, so every shade is dark 50 is #e4ecf8, 900 is #0a1623
Pale blue hsl(210 80% 90%) 50 through 300 are all white 50 is #e4edf7, 900 is #0b1621

The generator works in OKLCH and keeps every shade inside sRGB. Seeds that are almost gray produce neutral families instead of picking up a random hue, and muted brand colors keep their tint.

Predictable contrast. With the default scale, any two shades five steps apart meet WCAG AA (4.5:1) against each other: 50/500, 100/600, 200/700, 300/800, 400/900. So a pairing like background: var(--ig-primary-100); color: var(--ig-primary-600) stays readable whatever the brand color is. This applies to the numbered shades of the chromatic families (primary, secondary, info, success, warn, error).

The gray family is the exception. It keeps the library's original rhythm (material), so existing component styling doesn't shift. Only 3 of those 5 pairings meet AA on white; use $scales: ('gray': 'even') if you want all five.

Custom scales

palette() has a new $scales argument for choosing how shades are distributed:

// A preset for one family
$p: palette(#0099ff, #df1b74, #fff, $scales: ('gray': 'carbon'));

// Your own: a contrast range from 50 to 900, plus an optional cubic-bezier curve
$p: palette(#0099ff, #df1b74, #fff, $scales: (
  'primary': (range: 1.1 14, curve: 0.5 0 0.8 0.8)
));
  • Presets: even (the default), material (the original grayscale), tailwind (fitted to Tailwind v4 slate) and carbon (fitted to IBM Carbon gray). They copy those systems' rhythm, not their colors.
  • A custom range or curve can break the five-step AA rule. That's expected; check your pairings.
  • Gray takes an anchor. 'surface' (the default for every scale except material) measures contrast from your background, so in dark themes gray-50 stays close to the page and higher numbers get lighter. 'white' measures from white, which keeps the same ten grays in every theme. Quote the value: unquoted white is a color, and Sass will reject it.
  • If you ask for more contrast than your background allows (say, a 21:1 gray on a dark gray page), you get the closest reachable shade and a compile warning that tells you how far short it fell.

Surfaces

The surface family now has named layers instead of ten numbered shades:

Token What it's for
--ig-surface-base The page background (your surface color)
--ig-surface-sunken Recessed areas
--ig-surface-raised Cards and panels above the page
--ig-surface-overlay Menus, dialogs, popovers
--ig-surface-container A translucent tint layered on top (3% black on light, 3% white on dark)

Each layer has a matching -contrast token. A layer that has no room to move resolves to base. On pure white, for example, raised and overlay are also white, so let borders or shadows do the separating.

Components now read --ig-surface-base instead of --ig-surface-500. In the built-in palettes the two hold the same color, so nothing changes visually. If you override the surface at runtime, for example with --ig-surface-500: #fafafa, components will no longer pick it up. Set --ig-surface-base instead.

Numbered surface shades are deprecated. --ig-surface-50 through --ig-surface-900 are still emitted, and in fitted palettes --ig-surface-500 points to --ig-surface-base. They'll be removed in a future major, so move your own styles to the roles.

Smaller API changes

  • color() and contrast-color() default to base for the surface: color($color: 'surface') returns var(--ig-surface-base). Other families still default to 500.
  • $IPaletteColors now lists the surface roles instead of the numbered shades. If you loop over it and need every key a surface emits, use the new $ISurfaceShades.
  • New public lists: $INumericShades, $IAccentShades, $ISurfaceRoles, $ISurfaceNumericShades, $ISurfaceShades.
  • New configurable variables: $shade-generator, $scales, $family-scales, $surface-roles, $surface-container-alpha, $saturation-floor.
  • The Tailwind theme exposes both the surface roles and the numbered surface shades.
  • contrast() and luminance() are unchanged and still available.

MCP server

  • create_palette and create_theme use the new generator by default and accept scales and generator.
  • New fit_color_scale tool: give it a color ladder from another system, a brand ramp or a screenshot (lightest first), and it returns a scales spec that reproduces that rhythm with your own colors.
  • New guidance/colors/scales resource that explains the presets and their trade-offs.
  • create_custom_palette with explicit surface colors now takes the roles (base, sunken, raised, overlay, container) instead of 50–900.
  • Removed the "this seed is too light or dark for a good palette" warnings, since the new generator handles those seeds.

29.1.1

Choose a tag to compare

@simeonoff simeonoff released this 21 Oct 10:39
b24cbc3

Sass

Deep Sass imports resolve through exports in Node and Vite.

The package exports map used an invalid subpath pattern (./sass/**/*.*), so plain deep imports such as @use 'igniteui-theming/sass/themes/schemas' failed in Vite with a "not exported" error. Node's own resolver (require.resolve, import.meta.resolve) rejected these paths too. The pattern is now ./sass/*, and these imports work. If you added aliases or relative-path workarounds for this, you can remove them. pkg: imports through Sass's NodePackageImporter already worked and are unaffected. (#626)

Cleaner CSS from sizable() and pad().

When you pass plain numbers, these functions now put the value directly into the generated expression (e.g. var(--is-small, 1) * 2.5rem) instead of wrapping each one in max(2.5rem, -1 * 2.5rem). Values from var() and calc() behave as before. Rendering doesn't change, but the CSS is smaller and easier to read in DevTools. (#629)

Corrected parameter names in the theme docs.

The docs for three themes listed parameters under the wrong names. If you copied one of these from the docs, compilation failed with No parameter named …:

  • chip: $selected-outlined-background (not $outlined-selected-background)
  • grid: $body-column-border-color-odd / $body-column-border-color-even (not $body-column-border-color)
  • input: $input-suffix-background--filled (not $input-suffix-background-filled)

The docs also note that chips with a variant (primary, info, success, warning, danger) override $background, $text-color and $border-color. (#623)

MCP server

Generated themes compile for Web Components, React and Blazor.

Before this release, Sass from create_theme and create_component_theme could fail to compile outside Angular:

  • create_theme referenced palette metadata that it never imported. It now writes the design system out directly.
  • create_component_theme didn't import the theme schemas on non-Angular platforms.
  • Typography output wrapped Sass variables in quotes, so a literal '$typeface' ended up in your CSS. It also quoted custom font stacks that were already quoted.

Every style-generating tool now has a test that compiles its output, Angular included. (#628)

Clearer errors for invalid colors.

create_palette and create_theme now check color inputs. Palette failures are reported as errors, so your AI assistant sees that the call failed instead of getting broken output. (#628)

Correct CSS variable names for shadow tokens.

Some themes take a *-shadow parameter but emit the CSS variable as *-elevation, for example resting-shadow becomes --ig-button-resting-elevation. Token lookups now return the real variable name, so overrides suggested by the assistant actually apply. This covers badge, bottom nav, button group, contained and FAB buttons, card, carousel, chip, color picker, dialog and others. (#623)

Color Picker, QR Code and Breadcrumbs support.

The MCP server now knows these components, so it can look up their tokens and generate themes for them. (#622)

30.0.0 Beta 1

30.0.0 Beta 1 Pre-release
Pre-release

Choose a tag to compare

@simeonoff simeonoff released this 12 Oct 11:24
3a73048

Full Changelog: v29.1.0...v30.0.0-beta.1

29.1.0

Choose a tag to compare

@simeonoff simeonoff released this 06 Oct 12:05
6cb8135

What's Changed

Full Changelog: v29.0.0...v29.1.0

29.0.0

Choose a tag to compare

@simeonoff simeonoff released this 11 Sep 09:42
d9ac1a7

What's Changed

Breaking Changes

  • refactor(grid-summary): remove potentially harmful grid summary border-width properties by @desig9stein in #605

Full Changelog: v28.1.1...v29.0.0

29.0.0 Beta 2

29.0.0 Beta 2 Pre-release
Pre-release

Choose a tag to compare

@simeonoff simeonoff released this 11 Sep 08:24
d9ac1a7

What's Changed

Full Changelog: v29.0.0-beta.1...v29.0.0-beta.2

29.0.0 Beta 1

29.0.0 Beta 1 Pre-release
Pre-release

Choose a tag to compare

@simeonoff simeonoff released this 09 Sep 09:51
66b3453

What's Changed

  • feat(breadcrumbs): additional schema changes by @adrianptrv in #604
  • refactor(grid-summary): remove potentially harmful grid summary border-width properties by @desig9stein in #605

Full Changelog: v28.2.0-beta.1...v29.0.0-beta.1