Skip to content

Commit 53771f2

Browse files
committed
feat(platform-browser): add CssVarNamespacer
Adds the CssVarNamespacer utility service for prefixing CSS variables with MicA namespace dynamically.
1 parent 0c307b4 commit 53771f2

4 files changed

Lines changed: 100 additions & 0 deletions

File tree

goldens/public-api/platform-browser/index.api.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,15 @@ export class By {
5454
// @public
5555
export function createApplication(options?: ApplicationConfig, context?: BootstrapContext): Promise<ApplicationRef>;
5656

57+
// @public
58+
export class CssVarNamespacer {
59+
namespace(name: string): string;
60+
// (undocumented)
61+
static ɵfac: i0.ɵɵFactoryDeclaration<CssVarNamespacer, never>;
62+
// (undocumented)
63+
static ɵprov: i0.ɵɵInjectableDeclaration<CssVarNamespacer>;
64+
}
65+
5766
// @public
5867
export function disableDebugTools(): void;
5968

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import {inject, Injectable} from '@angular/core';
10+
import {CSS_VAR_NAMESPACE} from './dom_renderer';
11+
12+
/**
13+
* A service that can be used to manually namespace CSS variable names at runtime.
14+
* This is useful when reading or setting CSS variables dynamically in JavaScript that
15+
* were transformed by the compiler during the build.
16+
*
17+
* @publicApi
18+
*/
19+
@Injectable({providedIn: 'root'})
20+
export class CssVarNamespacer {
21+
private readonly namespacePrefix = inject(CSS_VAR_NAMESPACE, {optional: true}) ?? '';
22+
23+
/**
24+
* Prepends the namespace prefix to a CSS variable name.
25+
*
26+
* @param name The CSS variable name to namespace, including the leading `--`.
27+
* @returns The namespaced CSS variable name, including the leading `--`. Returns the input
28+
* unchanged if no namespace is configured.
29+
*/
30+
namespace(name: string): string {
31+
// Validate that the whole `--foo` variable is passed in.
32+
if (typeof ngDevMode === 'undefined' || ngDevMode) {
33+
if (!name.startsWith('--')) {
34+
throw new Error(
35+
`CSS variable names passed to \`CssVarNamespacer\` must start with '--', got: '${name}'`,
36+
);
37+
}
38+
}
39+
40+
// We want to support libraries which might be used by applications which do and don't
41+
// namespace variables. Therefore the library always needs to use `CssVarNamespacer`, even
42+
// though the application may not actually be namespacing anything.
43+
if (!this.namespacePrefix) return name;
44+
45+
return `--${this.namespacePrefix}${name.substring('--'.length)}`;
46+
}
47+
}

packages/platform-browser/src/platform-browser.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export {
1717
export {Meta, MetaDefinition} from './browser/meta';
1818
export {Title} from './browser/title';
1919
export {disableDebugTools, enableDebugTools} from './browser/tools/tools';
20+
export {CssVarNamespacer} from './dom/css_var_namespacer';
2021
export {By} from './dom/debug/by';
2122
export {REMOVE_STYLES_ON_COMPONENT_DESTROY} from './dom/dom_renderer';
2223
export {EVENT_MANAGER_PLUGINS, EventManager} from './dom/events/event_manager';
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import {TestBed} from '@angular/core/testing';
10+
import {CssVarNamespacer} from '../../src/dom/css_var_namespacer';
11+
import {provideCssVarNamespacing} from '../../src/dom/dom_renderer';
12+
13+
describe('CssVarNamespacer', () => {
14+
it('should namespace variables when `CSS_VAR_NAMESPACE` is provided', () => {
15+
TestBed.configureTestingModule({
16+
providers: [CssVarNamespacer, provideCssVarNamespacing('test-app_')],
17+
});
18+
19+
const namespacer = TestBed.inject(CssVarNamespacer);
20+
21+
expect(namespacer.namespace('--my-var')).toBe('--test-app_my-var');
22+
});
23+
24+
it('should not namespace variables when `CSS_VAR_NAMESPACE` is not provided', () => {
25+
TestBed.configureTestingModule({
26+
providers: [CssVarNamespacer],
27+
});
28+
29+
const namespacer = TestBed.inject(CssVarNamespacer);
30+
31+
expect(namespacer.namespace('--my-var')).toBe('--my-var');
32+
});
33+
34+
it('throws an error when a variable is passed in without the leading `--`', () => {
35+
TestBed.configureTestingModule({
36+
providers: [CssVarNamespacer],
37+
});
38+
39+
const namespacer = TestBed.inject(CssVarNamespacer);
40+
41+
expect(() => namespacer.namespace('my-var')).toThrowError(/must start with '--'/);
42+
});
43+
});

0 commit comments

Comments
 (0)