@for (additionalApiDoc of docItem.additionalApiDocs; track additionalApiDoc) {
}
diff --git a/src/app/pages/component-viewer/component-overview.html b/src/app/pages/component-viewer/component-overview.html
index a962a3a6..aa152f39 100644
--- a/src/app/pages/component-viewer/component-overview.html
+++ b/src/app/pages/component-viewer/component-overview.html
@@ -2,7 +2,7 @@
diff --git a/src/app/pages/guide-viewer/guide-viewer.html b/src/app/pages/guide-viewer/guide-viewer.html
index 2e504ead..cb827c69 100644
--- a/src/app/pages/guide-viewer/guide-viewer.html
+++ b/src/app/pages/guide-viewer/guide-viewer.html
@@ -2,7 +2,7 @@
diff --git a/src/app/pages/system-variables/index.ts b/src/app/pages/system-variables/index.ts
new file mode 100644
index 00000000..3a226db5
--- /dev/null
+++ b/src/app/pages/system-variables/index.ts
@@ -0,0 +1 @@
+export * from './system-variables';
diff --git a/src/app/pages/system-variables/system-variables.html b/src/app/pages/system-variables/system-variables.html
new file mode 100644
index 00000000..a3174523
--- /dev/null
+++ b/src/app/pages/system-variables/system-variables.html
@@ -0,0 +1,242 @@
+
+ Angular Material components depend on system variables defined as CSS variables through the
+ material.theme
+ Sass mixin. This page provides guidance and documentation for using these variables to
+ customize components.
+
+
+
Colors
+
+
+ Material Design uses color to create accessible, personal color schemes
+ that communicate your product's hierarchy, state, and brand. See Material
+ Design's Color System
+ page to learn more about its use and purpose.
+
+
+ The following colors are the most often used in Angular Material components. Use these
+ colors and follow their uses to add theme colors to your application's custom components.
+
+
+
+
+
+
Primary
+
--mat-sys-primary
+
+
+
+ The most common color used by Angular Material components to
+ participate in the application theme.
+
+
+ Examples include the background color
+ of filled buttons, the icon color of selected radio buttons, and the
+ outline color of form fields.
+
+
+ Use the color --mat-sys-on-primary for
+ icons, text, and other visual elements placed on a primary background. This
+ color is calculated to be optimal for accessibility and legibility.
+
+
+
+
+
+
+
Surface
+
--mat-sys-surface
+
+
+
+ A low-emphasis background color that provides a clear contrast for
+ both light and dark themes and their varied theme colors.
+
+
+ Examples include the background color of the application and most
+ components such as the dialog, card, table, and more.
+
+
+ Use the color --mat-sys-on-surface for
+ icons, text, and other visual elements placed on a surface background. This
+ color is calculated to be optimal for accessibility and legibility.
+
+
+
+
+
+
+
Error
+
--mat-sys-error
+
+
+
+ High-contrast color meant to alert the user to attract immediate attention.
+
+
+ Examples include the background color of the badge and the text color of invalid
+ form fields inputs.
+
+
+ Use the color --mat-sys-on-error for
+ icons, text, and other visual elements placed on an error background. This
+ color is calculated to be optimal for accessibility and legibility.
+
+
+
+
+
+
+
Outline
+
--mat-sys-outline
+
+
+
+ Used for borders and dividers to help provide visual separation between
+ and around elements.
+
+
+ Examples include the color of the divider and border color of an outlined
+ form field.
+
+
+ Use the color --mat-sys-outline-variant for a less
+ prominent outline.
+
+
+
+
+
+
+ Other available colors
+
+
+ These colors are less commonly used in Angular Material components but
+ are available for adding color variety and creating additional emphasis
+ to components.
+
+
+ Colors may be paired with a --mat-sys-on- variable
+ that should be used for text and icons placed within a filled container.
+
+
+ Alternative Theme Colors
+
+
+
+ Surface Colors
+
+
+ The following colors should be used for backgrounds and large,
+ low-emphasis areas of the screen.
+
+
+
+ Containers filled with a surface color should apply the
+ --mat-sys-on-surface color to text
+ and icons placed within.
+
+
+
+
+ Fixed Colors
+
+
+ These colors are the same for both light and dark themes. They are unused
+ by any Angular Material components.
+
+
+
+
+
+
+
Typography
+
+
+ There are five categories of font types defined by Material Design: body, display, headline,
+ label, and title. Each category has three sizes: small, medium, and large.
+
+
+ Learn more about how these categories and their sizes should be used in your application by
+ visiting Material Design's
+ Typography documentation.
+
+
+
+@for (category of ['body', 'display', 'headline', 'label', 'title']; track $index) {
+
+
{{category}}
+ @for (size of ['small', 'medium', 'large']; track $index) {
+
+
+
--mat-sys-{{category}}-{{size}}
+
+
Lorem ipsum dolor
+
+ }
+
+}
+
+
+ Each system variable can be applied to the "font" CSS style. Additionally, the parts of the variable definition
+ can be accessed individually by appending the keywords "font", "line-height", "size", "tracking", and "weight".
+
+
+ For example, the values for medium body text may be defined as follows:
+
+
+--mat-sys-body-medium: 400 0.875rem / 1.25rem Roboto, sans-serif;
+--mat-sys-body-medium-font: Roboto, sans-serif;
+--mat-sys-body-medium-line-height: 1.25rem;
+--mat-sys-body-medium-size: 0.875rem;
+--mat-sys-body-medium-tracking: 0.016rem;
+--mat-sys-body-medium-weight: 400;
+
+
+
Elevation
+
+
+ Material Design provides six levels of elevation that can be used to provide
+ a sense of depth and organization to an application's UI. Learn more at Material Design's
+ Elevation guide.
+
+
+
+ These levels are defined as CSS box-shadow values that can be styled to an element.
+
+
+@for (level of [0, 1, 2, 3, 4, 5]; track $index) {
+
+ box-shadow: var(--mat-sys-level{{level}})
+
+}
+
+
Overrides
+
+
+ The mat.theme-overrides mixin
+ can be included to emit different definitions for the system variables and
+ override the definitions emitted from mat.theme.
+
+
+
+ This example container has several system variables overridden by including the
+ following Sass code:
+
+
+ @include mat.theme-overrides((
+ primary: #ebdcff,
+ on-primary: #230f46,
+ body-medium: 500 1.15rem/1.3rem Arial,
+ corner-large: 32px,
+ level3: 0 4px 6px 1px var(--mat-sys-surface-dim),
+ ));
+
diff --git a/src/app/pages/system-variables/system-variables.scss b/src/app/pages/system-variables/system-variables.scss
new file mode 100644
index 00000000..77a04674
--- /dev/null
+++ b/src/app/pages/system-variables/system-variables.scss
@@ -0,0 +1,196 @@
+@use '@angular/material' as mat;
+
+:host {
+ display: block;
+ max-width: 1000px;
+}
+
+h1 {
+ font: var(--mat-sys-title-large);
+ font-size: 28px;
+ padding-top: 32px;
+}
+
+h2 {
+ font: var(--mat-sys-title-large);
+}
+
+a {
+ color: var(--mat-sys-primary);
+}
+
+.demo-warn {
+ background: var(--mat-sys-error-container);
+ color: var(--mat-sys-on-error-container);
+ border: 1px solid var(--mat-sys-outline-variant);
+ border-radius: var(--mat-sys-corner-extra-small);
+ padding: 8px;
+}
+
+.demo-group {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ grid-gap: 24px;
+ margin-top: 24px;
+}
+
+@media (max-width: 1000px) {
+ .demo-group { grid-template-columns: auto;}
+}
+
+.demo-color-container {
+ border-radius: var(--mat-sys-corner-small);
+ display: inline-block;
+ font: var(--mat-sys-body-medium);
+ vertical-align: top;
+}
+
+.demo-heading {
+ color: var(--mat-sys-on-primary);
+ background: var(--mat-sys-primary);
+ border: 1px solid var(--mat-sys-outline);
+ border-top-right-radius: var(--mat-sys-corner-small);
+ border-top-left-radius: var(--mat-sys-corner-small);
+ border-bottom: none;
+ padding: 16px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.demo-name {
+ font: var(--mat-sys-title-medium);
+}
+
+.demo-variable {
+ font: var(--mat-sys-title-small);
+ font-family: monospace;
+ text-align: right;
+}
+
+.demo-description {
+ border: 1px solid var(--mat-sys-outline);
+ border-bottom-right-radius: var(--mat-sys-corner-small);
+ border-bottom-left-radius: var(--mat-sys-corner-small);
+ padding: 0 16px;
+}
+
+.demo-code {
+ font-family: monospace;
+}
+
+.demo-surface-variable {
+ display: inline-block;
+ font-family: monospace;
+ background: var(--mat-sys-primary-container);
+ color: var(--mat-sys-on-primary-container);
+ padding: 2px 6px;
+ margin: 0 2px;
+ border-radius: 4px;
+}
+
+mat-expansion-panel {
+ margin-top: 24px;
+ overflow: visible;
+ @include mat.expansion-overrides((
+ 'container-text-font': var(--mat-sys-body-medium-font),
+ 'container-text-size': var(--mat-sys-body-medium-size),
+ 'container-text-weight': var(--mat-sys-body-medium-weight),
+ 'container-text-line-height': var(--mat-sys-body-medium-line-height),
+ 'container-text-tracking': var(--mat-sys-body-medium-tracking),
+ ));
+}
+
+.demo-compact-color-container {
+ border-radius: var(--mat-sys-corner-small);
+ border: 1px solid var(--mat-sys-outline);
+ overflow: hidden; // Hide child heading background color
+ margin-top: 24px;
+
+ .demo-heading {
+ border: none;
+ border-radius: 0;
+
+ &:not(:nth-child(1)) {
+ border-top: 1px solid var(--mat-sys-outline);
+ }
+ }
+
+ .demo-variables {
+ text-align: end;
+ }
+}
+
+.demo-typography-group {
+ border: 1px solid var(--mat-sys-outline);
+ border-radius: var(--mat-sys-corner-small);
+ margin-top: 40px;
+ overflow: hidden;
+}
+
+.demo-typography-title {
+ text-transform: capitalize;
+ font: var(--mat-sys-title-medium);
+ padding: 16px;
+ border-bottom: 1px solid var(--mat-sys-outline);
+ background: var(--mat-sys-primary-container);
+ color: var(--mat-sys-on-primary-container);
+}
+
+.demo-typography-variable {
+ min-width: 240px;
+}
+
+.demo-typography-example {
+ padding: 16px;
+ display: flex;
+ align-items: baseline;
+ border-top: 1px solid var(--mat-sys-outline-variant);
+
+ &:nth-child(1) {
+ border: none;
+ }
+ .demo-surface-variable {
+ margin-right: 16px;
+ }
+}
+
+.demo-typography-text {
+ display: inline-block;
+}
+
+.demo-elevation {
+ height: 40px;
+ width: 300px;
+ margin: 32px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: var(--mat-sys-surface-container);
+ color: var(--mat-sys-on-surface);
+ border-radius: var(--mat-sys-corner-extra-small);
+}
+
+.demo-code-block {
+ background: var(--mat-sys-surface-container-low);
+ padding: 16px;
+ border-radius: var(--mat-sys-corner-small);
+ border: 1px solid var(--mat-sys-outline);
+}
+
+.demo-overrides {
+ background-color: var(--mat-sys-primary);
+ color: var(--mat-sys-on-primary);
+ font: var(--mat-sys-body-medium);
+ border-radius: var(--mat-sys-corner-large);
+ box-shadow: var(--mat-sys-level3);
+ padding: 16px;
+
+ @include mat.theme-overrides((
+ primary: #ebdcff,
+ on-primary: #230f46,
+ body-medium: 500 1.15rem/1.3rem Arial,
+ corner-large: 32px,
+ level3: 0 4px 6px 1px var(--mat-sys-surface-dim),
+ ));
+}
diff --git a/src/app/pages/system-variables/system-variables.ts b/src/app/pages/system-variables/system-variables.ts
new file mode 100644
index 00000000..5f2f66fa
--- /dev/null
+++ b/src/app/pages/system-variables/system-variables.ts
@@ -0,0 +1,158 @@
+import {ChangeDetectionStrategy, Component, input} from '@angular/core';
+import {MatCardModule} from '@angular/material/card';
+import {MatExpansionModule} from '@angular/material/expansion';
+import {MatIconModule} from '@angular/material/icon';
+
+interface Color {
+ name: string;
+ background: string;
+ text: string;
+ hideText?: boolean;
+}
+
+@Component({
+ selector: 'theme-demo-colors',
+ template: `
+
+ @for (color of colors(); track $index) {
+
+
{{color.name}}
+
+
{{color.background}}
+ @if (!color.hideText) {
+
{{color.text}}
+ }
+
+
+ }
+
+ `,
+ styleUrl: 'system-variables.scss',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ standalone: true,
+})
+export class ThemeDemoColors {
+ colors = input
();
+}
+
+@Component({
+ selector: 'app-system-variables',
+ templateUrl: './system-variables.html',
+ styleUrls: ['./system-variables.scss'],
+ imports: [MatCardModule, MatExpansionModule, MatIconModule, ThemeDemoColors],
+ standalone: true,
+})
+export class SystemVariables {
+ alternativeThemeColors: Color[] = [
+ {
+ name: 'Primary Container',
+ background: '--mat-sys-primary-container',
+ text: '--mat-sys-on-primary-container',
+ },
+ {
+ name: 'Secondary',
+ background: '--mat-sys-secondary',
+ text: '--mat-sys-on-secondary',
+ },
+ {
+ name: 'Secondary Container',
+ background: '--mat-sys-secondary-container',
+ text: '--mat-sys-on-secondary-container',
+ },
+ {
+ name: 'Tertiary',
+ background: '--mat-sys-tertiary',
+ text: '--mat-sys-on-tertiary',
+ },
+ {
+ name: 'Tertiary Container',
+ background: '--mat-sys-tertiary-container',
+ text: '--mat-sys-on-tertiary-container',
+ },
+ {
+ name: 'Error Container',
+ background: '--mat-sys-error-container',
+ text: '--mat-sys-on-error-container',
+ },
+ ];
+
+ surfaceColors: Color[] = [
+ {
+ name: 'Surface Dim',
+ background: '--mat-sys-surface-dim',
+ text: '--mat-sys-on-surface',
+ hideText: true,
+ },
+ {
+ name: 'Surface Bright',
+ background: '--mat-sys-surface-bright',
+ text: '--mat-sys-on-surface',
+ hideText: true,
+ },
+ {
+ name: 'Surface Container Lowest',
+ background: '--mat-sys-surface-container-lowest',
+ text: '--mat-sys-on-surface',
+ hideText: true,
+ },
+ {
+ name: 'Surface Container Low',
+ background: '--mat-sys-surface-container-low',
+ text: '--mat-sys-on-surface',
+ hideText: true,
+ },
+ {
+ name: 'Surface Container',
+ background: '--mat-sys-surface-container',
+ text: '--mat-sys-on-surface',
+ hideText: true,
+ },
+ {
+ name: 'Surface Container High',
+ background: '--mat-sys-surface-container-high',
+ text: '--mat-sys-on-surface',
+ hideText: true,
+ },
+ {
+ name: 'Surface Container Highest',
+ background: '--mat-sys-surface-container-highest',
+ text: '--mat-sys-on-surface',
+ hideText: true,
+ },
+ ];
+
+ fixedColors: Color[] = [
+ {
+ name: 'Primary Fixed',
+ background: '--mat-sys-primary-fixed',
+ text: '--mat-sys-on-primary-fixed',
+ },
+ {
+ name: 'Primary Fixed Dim',
+ background: '--mat-sys-primary-fixed-dim',
+ text: '--mat-sys-on-primary-fixed',
+ },
+ {
+ name: 'Secondary Fixed',
+ background: '--mat-sys-secondary-fixed',
+ text: '--mat-sys-on-secondary-fixed',
+ },
+ {
+ name: 'Secondary Fixed Dim',
+ background: '--mat-sys-secondary-fixed-dim',
+ text: '--mat-sys-on-secondary-fixed',
+ },
+ {
+ name: 'Tertiary Fixed',
+ background: '--mat-sys-tertiary-fixed',
+ text: '--mat-sys-on-tertiary-fixed',
+ },
+ {
+ name: 'Tertiary Fixed Dim',
+ background: '--mat-sys-tertiary-fixed-dim',
+ text: '--mat-sys-on-tertiary-fixed',
+ },
+ ];
+}
diff --git a/src/app/shared/doc-viewer/doc-viewer.spec.ts b/src/app/shared/doc-viewer/doc-viewer.spec.ts
index 7ddf865e..c8b0c290 100644
--- a/src/app/shared/doc-viewer/doc-viewer.spec.ts
+++ b/src/app/shared/doc-viewer/doc-viewer.spec.ts
@@ -33,6 +33,15 @@ describe('DocViewer', () => {
expect(docViewer.nativeElement.innerHTML).toBe('my docs page
');
});
+ it('should load component', () => {
+ const fixture = TestBed.createComponent(DocViewerWithCompTestComponent);
+ fixture.detectChanges();
+
+ const docViewer = fixture.debugElement.query(By.directive(DocViewer));
+ expect(docViewer).not.toBeNull();
+ expect(docViewer.nativeElement.innerHTML).toContain(`TEST_COMPONENT_GUIDE`);
+ });
+
it('should save textContent of the doc', () => {
const fixture = TestBed.createComponent(DocViewerTestComponent);
fixture.detectChanges();
@@ -145,7 +154,7 @@ describe('DocViewer', () => {
@Component({
selector: 'test',
- template: ``,
+ template: ``,
standalone: true,
imports: [DocViewerModule, DocsAppTestingModule],
})
@@ -170,3 +179,19 @@ const FAKE_DOCS: {[key: string]: string} = {
'',
/* eslint-enable @typescript-eslint/naming-convention */
};
+
+@Component({
+ template: `TEST_COMPONENT_GUIDE`,
+ standalone: true,
+})
+class TestComponent {}
+
+@Component({
+ selector: 'test',
+ template: ``,
+ standalone: true,
+ imports: [DocViewerModule, DocsAppTestingModule, TestComponent],
+})
+class DocViewerWithCompTestComponent {
+ component = TestComponent;
+}
diff --git a/src/app/shared/doc-viewer/doc-viewer.ts b/src/app/shared/doc-viewer/doc-viewer.ts
index 8690fad8..a4e23d64 100644
--- a/src/app/shared/doc-viewer/doc-viewer.ts
+++ b/src/app/shared/doc-viewer/doc-viewer.ts
@@ -1,4 +1,10 @@
-import {ComponentPortal, DomPortalOutlet} from '@angular/cdk/portal';
+import {
+ ComponentType,
+ ComponentPortal,
+ DomPortalOutlet,
+ Portal,
+ PortalModule
+} from '@angular/cdk/portal';
import {HttpClient, HttpErrorResponse} from '@angular/common/http';
import {DomSanitizer} from '@angular/platform-browser';
import {
@@ -40,20 +46,37 @@ class DocFetcher {
@Component({
selector: 'doc-viewer',
- template: 'Loading document...',
+ template: `
+ @if (portal) {
+
+ } @else {
+ Loading document...
+ }
+ `,
standalone: true,
+ imports: [PortalModule]
})
export class DocViewer implements OnDestroy {
private _portalHosts: DomPortalOutlet[] = [];
private _documentFetchSubscription: Subscription | undefined;
+ protected portal: Portal | undefined;
readonly name = input();
- /** The URL of the document to display. */
+ /** The document to display, either as a URL to a markdown file or a component to create. */
@Input()
- set documentUrl(url: string | undefined) {
- if (url !== undefined) {
- this._fetchDocument(url);
+ set document(document: string | ComponentType | undefined) {
+ if (typeof document === 'string') {
+ this._fetchDocument(document);
+ } else if (document) {
+ this.portal = new ComponentPortal(document);
+
+ // Resolving and creating components dynamically in Angular happens synchronously, but since
+ // we want to emit the output if the components are actually rendered completely, we wait
+ // until the Angular zone becomes stable.
+ this._ngZone.onStable
+ .pipe(take(1))
+ .subscribe(() => this.contentRendered.next(this._elementRef.nativeElement));
}
}
diff --git a/src/app/shared/example-viewer/code-snippet.html b/src/app/shared/example-viewer/code-snippet.html
index 09b54722..61de6b25 100644
--- a/src/app/shared/example-viewer/code-snippet.html
+++ b/src/app/shared/example-viewer/code-snippet.html
@@ -1,3 +1,3 @@
diff --git a/src/app/shared/guide-items/guide-items.ts b/src/app/shared/guide-items/guide-items.ts
index 43563532..4790b901 100644
--- a/src/app/shared/guide-items/guide-items.ts
+++ b/src/app/shared/guide-items/guide-items.ts
@@ -1,13 +1,15 @@
import {Injectable} from '@angular/core';
+import {SystemVariables} from '../../pages/system-variables';
+import {ComponentType} from '@angular/cdk/portal';
export interface GuideItem {
id: string;
name: string;
- document: string;
overview: string;
+ document: string | ComponentType;
}
-const GUIDES = [
+const GUIDES: GuideItem[] = [
{
id: 'getting-started',
name: 'Getting started',
@@ -26,6 +28,12 @@ const GUIDES = [
document: '/docs-content/guides/theming.html',
overview: 'Customize your application with Angular Material\'s theming system.'
},
+ {
+ id: 'system-variables',
+ name: 'System Variables',
+ document: SystemVariables,
+ overview: 'Understand the system variables available to use in your application.'
+ },
{
id: 'theming-your-components',
name: 'Theming your own components',
diff --git a/src/app/shared/table-of-contents/table-of-contents.ts b/src/app/shared/table-of-contents/table-of-contents.ts
index 9bdc7814..5a9c4d60 100644
--- a/src/app/shared/table-of-contents/table-of-contents.ts
+++ b/src/app/shared/table-of-contents/table-of-contents.ts
@@ -130,7 +130,7 @@ export class TableOfContents implements OnInit, AfterViewInit, OnDestroy {
id: header.id,
active: false
};
- });
+ }).filter(link => link.id);
this._linkSections[sectionIndex] = {name: sectionName, links};
this._links.push(...links);
diff --git a/src/styles.scss b/src/styles.scss
index e4c7791e..6febe002 100644
--- a/src/styles.scss
+++ b/src/styles.scss
@@ -6,6 +6,9 @@
@use './styles/general';
html {
+ background-color: var(--mat-sys-surface);
+ color: var(--mat-sys-on-surface);
+
@include mat.theme((
color: (
theme-type: light,