Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
80f990d
refactor(migrations): rebuild Sass migrations on a shared stylesheet …
simeonoff Oct 12, 2026
044ad4f
migrations(theming): pin palette() calls to the legacy generator in 2…
simeonoff Oct 12, 2026
a277f55
chore(demos): show surface roles in the color sample
simeonoff Oct 12, 2026
15e359c
feat(themes): update to Ignite UI Theming 30.0.0
simeonoff Oct 12, 2026
accd061
refactor(migrations): address review comments
simeonoff Oct 12, 2026
12436f4
fix(migrations): handle indented declarations and interpolations when…
simeonoff Oct 12, 2026
65a3a29
fix(migrations): remove the bare includes of a removed module used wi…
simeonoff Oct 12, 2026
c207e3a
fix(migrations): rename variables that older changes name without the…
simeonoff Oct 12, 2026
9419a2e
chore(migrations): target the palette generator migration at 22.3.0
simeonoff Oct 14, 2026
8c0b281
fix(migrations): skip escaped characters in unquoted url() tokens
simeonoff Oct 14, 2026
44ad1c4
Merge branch 'master' into simeonoff/theming/version-30.0.0
simeonoff Oct 20, 2026
5e5ba16
fix(migrations): read url() and interpolations in strings as Sass does
simeonoff Oct 20, 2026
bdfbd19
docs(migrations): add review instructions for the Sass migrations
simeonoff Oct 20, 2026
0f57aab
Merge branch 'master' into simeonoff/theming/version-30.0.0
simeonoff Oct 20, 2026
296e6fe
fix(migrations): skip comments after arguments and in interpolations
simeonoff Oct 20, 2026
3f357cd
build(deps): bump igniteui-theming to 30.0.0-beta.2
simeonoff Oct 21, 2026
4d0424b
Merge remote-tracking branch 'origin/master' into simeonoff/theming/v…
simeonoff Oct 21, 2026
eaa154b
build(deps): bump igniteui-theming to 30.0.0
simeonoff Oct 22, 2026
ffea0b2
Merge branch 'master' into simeonoff/theming/version-30.0.0
simeonoff Oct 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/instructions/sass-migrations.instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
applyTo: "projects/igniteui-angular/migrations/**"
---

# Sass migrations

The `ng update` migrations change Sass stylesheets through a text scanner in `migrations/common/sass/`, not a Sass parser. The scanner tells code from non-code the way Sass does, and the changes in `changes.ts` work on the code it finds. Both files open with a comment that states what they handle, and `changes.ts` lists its known limits.

## Reviewing

- Treat the known limits in `changes.ts` as intentional. Don't report a case they already cover, such as a call through another module's namespace, a dynamic reference through `meta.get-function()`, a name written with escapes, or a nested call to the same member in `updateArgument`.
- Report a scanning or matching problem only when it can happen in a stylesheet an Angular application would contain, and say what that stylesheet looks like. Input that the Sass compiler rejects isn't a finding: check that the example compiles.
- Give the most weight to output that is wrong without anyone noticing: code that a change removes or rewrites by mistake, or a change that leaves a stylesheet that no longer compiles. A change that skips an unusual call and leaves it as it was is less severe, and a call it reports for manual review is working as intended.
- Don't suggest replacing the scanner with ad hoc regular expressions over the raw text. Regular expressions run on the code the scanner masks, so that strings and comments stay out of reach.

## Tests

- The context matrix in `common/sass/changes.spec.ts` runs every change against the places a statement can appear: inert ones, such as comments and strings, and non-code that comes before code, such as `url()` tokens and escaped strings. The Sass compiler validates every context first.
- A new scanning case belongs in that matrix as a context, so that it covers every change at once, rather than in a test for one change.
- A migration for a release has its own spec, next to its `index.ts`, that runs it end to end.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

All notable changes for each version of this project will be documented in this file.

## 22.3.0

### Themes

- **Breaking Change** - Updated to Ignite UI Theming 30.0.0. The `palette()` function builds palettes with the new `fitted` generator by default, which places every shade by contrast and emits it as a fixed color. The predefined palettes and the pre-built themes keep the `legacy` generator and are unchanged.
- `ng update` adds `$generator: 'legacy'` to existing `palette()` calls, so they keep the colors they produced before. Remove the argument to switch a palette to the `fitted` generator.
- The deprecated `light-theme`, `dark-theme`, and the other deprecated theme wrapper mixins build their palettes with the `legacy` generator.
- **Breaking Change** - Component themes take their background from the `base` surface role, `--ig-surface-base`, instead of `--ig-surface-500`. Override the surface roles (`--ig-surface-base`, `--ig-surface-raised`, `--ig-surface-overlay`, `--ig-surface-sunken`, `--ig-surface-container`) instead of `--ig-surface-500` to change the surface at runtime.
- The numbered surface shades, such as `--ig-surface-500`, are deprecated and will be removed in a future release. Palettes still emit them under both generators, and in a `fitted` palette `--ig-surface-500` points to `--ig-surface-base`.

## Unreleased

### Behavioral Changes
Expand Down
10 changes: 5 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@
"express": "^5.2.1",
"fflate": "^0.8.1",
"igniteui-i18n-core": "^1.0.2",
"igniteui-theming": "^29.0.0",
"igniteui-trial-watermark": "^3.1.0",
"jspdf": "^4.2.1",
"lodash-es": "^4.17.21",
Expand Down Expand Up @@ -137,6 +136,7 @@
"igniteui-grid-lite": "~0.11.0",
"igniteui-i18n-resources": "^1.0.5",
"igniteui-sassdoc-theme": "^2.2.0",
"igniteui-theming": "^30.0.0",
"igniteui-webcomponents": "^7.4.0",
"lit-html": "^3.2.1",
"ng-packagr": "^22.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ $_theme: digest-schema($dark-bootstrap-checkbox);

@include m(disabled) {
@include e(composite) {
background: color($color: 'surface', $variant: 500);
background: color($color: 'surface');
}

@include e(composite-mark) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,7 @@
$error: color($palette, error),
$surface: if($surface != #fff, $surface, #fff),
$gray: if($gray != #9e9e9e, $gray, #000),
$generator: 'legacy',
);

@include theme(
Expand Down Expand Up @@ -633,6 +634,7 @@
$error: color($palette, error),
$surface: if($surface != #fff, $surface, #222),
$gray: if($gray != #9e9e9e, $gray, #fff),
$generator: 'legacy',
);

@include theme(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
$info: $info,
$success: $success,
$warn: $warn,
$error: $error
$error: $error,
$generator: 'legacy'
),
$schema: $light-bootstrap-schema,
$exclude: $exclude,
Expand Down Expand Up @@ -76,7 +77,8 @@
$info: $info,
$success: $success,
$warn: $warn,
$error: $error
$error: $error,
$generator: 'legacy'
),
$schema: $dark-bootstrap-schema,
$exclude: $exclude,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
$info: $info,
$success: $success,
$warn: $warn,
$error: $error
$error: $error,
$generator: 'legacy'
),
$schema: $light-fluent-schema,
$exclude: $exclude,
Expand Down Expand Up @@ -76,7 +77,8 @@
$info: $info,
$success: $success,
$warn: $warn,
$error: $error
$error: $error,
$generator: 'legacy'
),
$schema: $dark-fluent-schema,
$exclude: $exclude,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
$info: $info,
$success: $success,
$warn: $warn,
$error: $error
$error: $error,
$generator: 'legacy'
),
$schema: $light-indigo-schema,
$exclude: $exclude,
Expand Down Expand Up @@ -78,7 +79,8 @@
$info: $info,
$success: $success,
$warn: $warn,
$error: $error
$error: $error,
$generator: 'legacy'
),
$schema: $dark-indigo-schema,
$exclude: $exclude,
Expand Down
4 changes: 2 additions & 2 deletions projects/igniteui-angular/grids/themes/_base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1984,8 +1984,8 @@
padding-block: pad-block(4px, 6px, 8px);
padding-inline: pad-inline(4px, 8px, 16px);
min-width: rem(320px);
color: var(--ig-grid-foreground, contrast-color($color: 'surface', $variant: 500));
background: var(--ig-grid-background, color($color: 'surface', $variant: 500));
color: var(--ig-grid-foreground, contrast-color($color: 'surface'));
background: var(--ig-grid-background, color($color: 'surface'));
border-radius: 0;

igc-icon,
Expand Down
111 changes: 111 additions & 0 deletions projects/igniteui-angular/migrations/common/UpdateChanges.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2309,6 +2309,117 @@ $my-other-theme: my-namespace.function1($color1: contrast-color($palette: palett
expect(appTree.readContent('test.component.scss')).toBe(expectedFileContent);
});

it('should add a theme property only to exact calls of the owner, and report what it cannot change', () => {
const themeChangesJson: ThemeChanges = {
changes: [
{
name: '$generator', add: `'legacy'`, type: ThemeType.Property,
owner: 'palette', exactOwner: true, ownerType: ThemeType.Function, parameterIndex: 10
},
{ name: 'card-typography', type: ThemeType.Mixin, remove: true }
]
};
const jsonPath = path.join(__dirname, 'changes', 'theme-changes.json');
vi.spyOn(fs, 'existsSync').mockImplementation((filePath: fs.PathLike) => filePath === jsonPath);
vi.spyOn(fs, 'readFileSync').mockImplementation(() => JSON.stringify(themeChangesJson));

appTree.create('styles.scss',
`$a: palette(red, blue, white);
$b: color-palette(red);
$c: palette($args...);
@include palette($a);
.card { @include card-typography { color: red; } }
`);
appTree.create('theme.sass',
`$a: palette(red, blue, white)
:root
+palette($a)
`);

const warn = vi.fn();
const context = { logger: { warn, info: () => {} } } as any;
new UnitUpdateChanges(__dirname, appTree, context).applyChanges();

expect(appTree.readContent('styles.scss')).toEqual(
`$a: palette(red, blue, white, $generator: 'legacy');
$b: color-palette(red);
$c: palette($args...);
@include palette($a);
.card { @include card-typography { color: red; } }
`);
expect(appTree.readContent('theme.sass')).toEqual(
`$a: palette(red, blue, white, $generator: 'legacy')
:root
+palette($a)
`);
const warnings = warn.mock.calls.map(args => args[0]);
expect(warnings).toContain(` - /styles.scss:3 - Add $generator: 'legacy' to the arguments this call of palette() spreads.`);
expect(warnings).toContain(' - /styles.scss:5 - Remove the include of the mixin card-typography, which passes a content block.');
});

it('should rename variables that older changes name without their $', () => {
const themeChangesJson: ThemeChanges = {
changes: [
{ name: 'igx-elevation', replaceWith: 'elevation', type: ThemeType.Variable },
{ name: 'igx-elevations', replaceWith: 'elevations', type: ThemeType.Variable }
]
};
const jsonPath = path.join(__dirname, 'changes', 'theme-changes.json');
vi.spyOn(fs, 'existsSync').mockImplementation((filePath: fs.PathLike) => filePath === jsonPath);
vi.spyOn(fs, 'readFileSync').mockImplementation(() => JSON.stringify(themeChangesJson));

appTree.create('styles.scss', `$a: $igx-elevations;\n$b: map.get($igx-elevations, $igx-elevation);\n`);
new UnitUpdateChanges(__dirname, appTree).applyChanges();

expect(appTree.readContent('styles.scss')).toEqual(`$a: $elevations;\n$b: map.get($elevations, $elevation);\n`);
});

it('should remove variables marked for removal instead of renaming them', () => {
const themeChangesJson: ThemeChanges = {
changes: [
{ name: '$browser-context', remove: true, type: ThemeType.Variable },
{ name: '$_indigo-elevation-navbar', replaceWith: '', type: ThemeType.Variable }
]
};
const jsonPath = path.join(__dirname, 'changes', 'theme-changes.json');
vi.spyOn(fs, 'existsSync').mockImplementation((filePath: fs.PathLike) => filePath === jsonPath);
vi.spyOn(fs, 'readFileSync').mockImplementation(() => JSON.stringify(themeChangesJson));

appTree.create('styles.scss', `$browser-context: 16px;\n$_indigo-elevation-navbar: 2;\n.a { width: math.div(10px, $browser-context); }\n`);

const warn = vi.fn();
new UnitUpdateChanges(__dirname, appTree, { logger: { warn, info: () => {} } } as any).applyChanges();

expect(appTree.readContent('styles.scss')).toEqual(`.a { width: math.div(10px, $browser-context); }\n`);
expect(warn).toHaveBeenCalledWith(' - /styles.scss:1 - Replace this reference to the removed variable $browser-context.');
});

it('should report what it cannot change at its line in the migrated stylesheet', () => {
const themeChangesJson: ThemeChanges = {
changes: [
{ name: '$generator', add: `'legacy'`, type: ThemeType.Property, owner: 'palette', exactOwner: true },
{ name: 'card-typography', type: ThemeType.Mixin, remove: true }
]
};
const jsonPath = path.join(__dirname, 'changes', 'theme-changes.json');
vi.spyOn(fs, 'existsSync').mockImplementation((filePath: fs.PathLike) => filePath === jsonPath);
vi.spyOn(fs, 'readFileSync').mockImplementation(() => JSON.stringify(themeChangesJson));

// the include goes after the palette change is made, and takes three lines with it
appTree.create('styles.scss',
`@include card-typography(
$categories: (title: 'h5')
);
$c: palette($args...);
`);

const warn = vi.fn();
new UnitUpdateChanges(__dirname, appTree, { logger: { warn, info: () => {} } } as any).applyChanges();

expect(appTree.readContent('styles.scss')).toEqual(`$c: palette($args...);\n`);
expect(warn).toHaveBeenCalledWith(` - /styles.scss:1 - Add $generator: 'legacy' to the arguments this call of palette() spreads.`);
});

it('should remove @use imports for deleted scss modules along with their member access', () => {
const scssUseChangesJson: ScssUseChanges = {
changes: [
Expand Down
Loading
Loading