From 111a7644ef166cd66ebfd1472a287e5d427b9d31 Mon Sep 17 00:00:00 2001 From: Amy Sorto <8575252+amysorto@users.noreply.github.com> Date: Fri, 17 Jan 2025 21:54:18 +0000 Subject: [PATCH] docs: update overview page --- .../docs-viewer/docs-viewer.component.ts | 9 +- .../docs-card/docs-card-container.mts | 55 ++++- .../marked/extensions/docs-card/docs-card.mts | 26 ++- .../extensions/docs-card/docs-nav-card.mts | 146 +++++++++++++ .../extensions/docs-decorative-header.mts | 72 +++++-- .../pipeline/shared/marked/parse.mts | 3 + .../docs-card-container.md | 4 +- adev/shared-docs/styles/_colors.scss | 63 +++++- adev/shared-docs/styles/docs/_card.scss | 176 +++++++++++++++ .../styles/docs/_decorative-header.scss | 159 +++++++++----- .../src/app/features/docs/docs.component.html | 1 + .../src/app/features/docs/docs.component.scss | 4 + adev/src/app/features/docs/docs.component.ts | 8 +- adev/src/assets/icons/cli.svg | 7 + adev/src/assets/icons/components.svg | 6 + adev/src/assets/icons/dev-tools.svg | 6 + adev/src/assets/icons/di.svg | 6 + adev/src/assets/icons/docs.svg | 3 + adev/src/assets/icons/forms.svg | 6 + adev/src/assets/icons/language-service.svg | 8 + adev/src/assets/icons/ng-update.svg | 4 + adev/src/assets/icons/routing.svg | 4 + adev/src/assets/icons/signals.svg | 7 + adev/src/assets/icons/ssr.svg | 7 + adev/src/assets/icons/star.svg | 11 + adev/src/assets/icons/tutorials.svg | 8 + adev/src/assets/images/browser-bolt.svg | 25 +++ adev/src/assets/images/community.svg | 67 ++++++ .../assets/images/editor-dark-horizontal.svg | 103 --------- .../assets/images/editor-dark-vertical.svg | 103 --------- .../assets/images/editor-light-horizontal.svg | 103 --------- .../assets/images/editor-light-vertical.svg | 103 --------- .../images/learn-angular-browser-bw.svg | 51 +++++ adev/src/assets/images/open-source.svg | 27 +++ adev/src/assets/images/rocket.svg | 20 ++ adev/src/assets/images/servers.svg | 56 +++++ adev/src/content/introduction/BUILD.bazel | 21 ++ .../content/introduction/what-is-angular.md | 203 ++++++++---------- 38 files changed, 1068 insertions(+), 623 deletions(-) create mode 100644 adev/shared-docs/pipeline/shared/marked/extensions/docs-card/docs-nav-card.mts create mode 100644 adev/src/assets/icons/cli.svg create mode 100644 adev/src/assets/icons/components.svg create mode 100644 adev/src/assets/icons/dev-tools.svg create mode 100644 adev/src/assets/icons/di.svg create mode 100644 adev/src/assets/icons/docs.svg create mode 100644 adev/src/assets/icons/forms.svg create mode 100644 adev/src/assets/icons/language-service.svg create mode 100644 adev/src/assets/icons/ng-update.svg create mode 100644 adev/src/assets/icons/routing.svg create mode 100644 adev/src/assets/icons/signals.svg create mode 100644 adev/src/assets/icons/ssr.svg create mode 100644 adev/src/assets/icons/star.svg create mode 100644 adev/src/assets/icons/tutorials.svg create mode 100644 adev/src/assets/images/browser-bolt.svg create mode 100644 adev/src/assets/images/community.svg delete mode 100644 adev/src/assets/images/editor-dark-horizontal.svg delete mode 100644 adev/src/assets/images/editor-dark-vertical.svg delete mode 100644 adev/src/assets/images/editor-light-horizontal.svg delete mode 100644 adev/src/assets/images/editor-light-vertical.svg create mode 100644 adev/src/assets/images/learn-angular-browser-bw.svg create mode 100644 adev/src/assets/images/open-source.svg create mode 100644 adev/src/assets/images/rocket.svg create mode 100644 adev/src/assets/images/servers.svg diff --git a/adev/shared-docs/components/viewers/docs-viewer/docs-viewer.component.ts b/adev/shared-docs/components/viewers/docs-viewer/docs-viewer.component.ts index f918cac579e..29af557152c 100644 --- a/adev/shared-docs/components/viewers/docs-viewer/docs-viewer.component.ts +++ b/adev/shared-docs/components/viewers/docs-viewer/docs-viewer.component.ts @@ -171,11 +171,18 @@ export class DocViewer { return; } - const firstHeading = element.querySelector('h2,h3[id]'); + let firstHeading = element.querySelector('h2,h3[id]'); if (!firstHeading) { return; } + // If the first header is in a card container element, place TOC element + // before the container. + const parentEl = firstHeading.parentElement; + if (parentEl && parentEl.classList.contains('docs-card-container-header')) { + firstHeading = parentEl.parentElement; + } + // Since the content of the main area is dynamically created and there is // no host element for a ToC component, we create it manually. let tocHostElement: HTMLElement | null = element.querySelector(TOC_HOST_ELEMENT_NAME); diff --git a/adev/shared-docs/pipeline/shared/marked/extensions/docs-card/docs-card-container.mts b/adev/shared-docs/pipeline/shared/marked/extensions/docs-card/docs-card-container.mts index 63134819332..9963e28016a 100644 --- a/adev/shared-docs/pipeline/shared/marked/extensions/docs-card/docs-card-container.mts +++ b/adev/shared-docs/pipeline/shared/marked/extensions/docs-card/docs-card-container.mts @@ -7,14 +7,23 @@ */ import {Tokens, Token, RendererThis, TokenizerThis} from 'marked'; +import {loadWorkspaceRelativeFile} from '../../helpers.mjs'; +import {formatHeading} from '../../transformations/heading.mjs'; interface DocsCardContainerToken extends Tokens.Generic { type: 'docs-card-container'; cards: string; + headerTitle?: string; + headerImgSrc?: string; tokens: Token[]; } -const cardContainerRule = /^(.*?)<\/docs-card-container>/s; +// Capture group 1: all attributes on the opening tag +// Capture group 2: all content between the open and close tags +const cardContainerRule = + /^[^<]*]*))?>((?:.(?!\/docs-card-container))*)<\/docs-card-container>/s; +const headerTitleRule = /headerTitle="([^"]*)"/; +const headerImgSrcRule = /headerImgSrc="([^"]*)"/; export const docsCardContainerExtension = { name: 'docs-card-container', @@ -26,11 +35,18 @@ export const docsCardContainerExtension = { const match = cardContainerRule.exec(src); if (match) { - const body = match[1]; + const attr = match[1] ? match[1].trim() : ''; + const headerTitle = headerTitleRule.exec(attr); + const headerImgSrc = headerImgSrcRule.exec(attr); + + const body = match[2].trim(); + const token: DocsCardContainerToken = { type: 'docs-card-container', raw: match[0], cards: body ?? '', + headerTitle: headerTitle ? headerTitle[1] : undefined, + headerImgSrc: headerImgSrc ? headerImgSrc[1] : undefined, tokens: [], }; this.lexer.blockTokens(token.cards, token.tokens); @@ -39,10 +55,35 @@ export const docsCardContainerExtension = { return undefined; }, renderer(this: RendererThis, token: DocsCardContainerToken) { - return ` -
- ${this.parser.parse(token.tokens)} -
- `; + return token.headerTitle + ? getContainerWithHeader(this, token) + : getStandardContainer(this, token); }, }; + +function getStandardContainer(renderer: RendererThis, token: DocsCardContainerToken) { + return ` +
+ ${renderer.parser.parse(token.tokens)} +
+ `; +} + +function getContainerWithHeader(renderer: RendererThis, token: DocsCardContainerToken) { + // We can assume that all illustrations are svg files + // We need to read svg content, instead of renering svg with `img`, + // cause we would like to use CSS variables to support dark and light mode. + let illustration = token.headerImgSrc ? loadWorkspaceRelativeFile(token.headerImgSrc!) : ''; + + return ` +
+
+ ${formatHeading({text: token.headerTitle!, depth: 2})} + ${illustration} +
+
+ ${renderer.parser.parse(token.tokens)} +
+
+ `; +} diff --git a/adev/shared-docs/pipeline/shared/marked/extensions/docs-card/docs-card.mts b/adev/shared-docs/pipeline/shared/marked/extensions/docs-card/docs-card.mts index ec2cd0d8ee4..457936a0c36 100644 --- a/adev/shared-docs/pipeline/shared/marked/extensions/docs-card/docs-card.mts +++ b/adev/shared-docs/pipeline/shared/marked/extensions/docs-card/docs-card.mts @@ -17,17 +17,19 @@ interface DocsCardToken extends Tokens.Generic { link?: string; href?: string; imgSrc?: string; + iconImgSrc?: string; // Need image since icons are custom tokens: Token[]; } // Capture group 1: all attributes on the opening tag // Capture group 2: all content between the open and close tags -const cardRule = /^[^<]*]*)>((?:.(?!\/docs-card))*)<\/docs-card>/s; +const cardRule = /^[^<]*]*))?>((?:.(?!\/docs-card))*)<\/docs-card>/s; const titleRule = /title="([^"]*)"/; const linkRule = /link="([^"]*)"/; const hrefRule = /href="([^"]*)"/; const imgSrcRule = /imgSrc="([^"]*)"/; +const iconImgSrcRule = /iconImgSrc="([^"]*)"/; export const docsCardExtension = { name: 'docs-card', @@ -39,11 +41,12 @@ export const docsCardExtension = { const match = cardRule.exec(src); if (match) { - const attr = match[1].trim(); + const attr = match[1] ? match[1].trim() : ''; const title = titleRule.exec(attr); const link = linkRule.exec(attr); const href = hrefRule.exec(attr); const imgSrc = imgSrcRule.exec(attr); + const iconImgSrc = iconImgSrcRule.exec(attr); const body = match[2].trim(); @@ -55,6 +58,7 @@ export const docsCardExtension = { href: href ? href[1] : undefined, link: link ? link[1] : undefined, imgSrc: imgSrc ? imgSrc[1] : undefined, + iconImgSrc: iconImgSrc ? iconImgSrc[1] : undefined, tokens: [], }; this.lexer.blockTokens(token.body, token.tokens); @@ -68,7 +72,23 @@ export const docsCardExtension = { }; function getStandardCard(renderer: RendererThis, token: DocsCardToken) { - if (token.href) { + if (token.iconImgSrc && token.href) { + // We can assume that all icons are svg files since they are custom. + // We need to read svg content, instead of renering svg with `img`, + // cause we would like to use CSS variables to support dark and light mode. + const icon = loadWorkspaceRelativeFile(token.iconImgSrc); + + return ` + +
+ ${icon} +

${token.title}

+ ${renderer.parser.parse(token.tokens)} +
+ ${token.link ? token.link : 'Learn more'} +
+ `; + } else if (token.href) { return `
diff --git a/adev/shared-docs/pipeline/shared/marked/extensions/docs-card/docs-nav-card.mts b/adev/shared-docs/pipeline/shared/marked/extensions/docs-card/docs-nav-card.mts new file mode 100644 index 00000000000..db48729e7ef --- /dev/null +++ b/adev/shared-docs/pipeline/shared/marked/extensions/docs-card/docs-nav-card.mts @@ -0,0 +1,146 @@ +/*! + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import {Tokens, Token, RendererThis, TokenizerThis} from 'marked'; +import {loadWorkspaceRelativeFile} from '../../helpers.mjs'; + +interface DocsNavCardToken extends Tokens.Generic { + type: 'docs-nav-card'; + title: string; + items: string; + tokens: Token[]; + iconImgSrc?: string; // Need image since icons are custom +} + +// Capture group 1: all attributes on the opening tag +// Capture group 2: all content between the open and close tags +const navCardRule = /^[^<]*]*)>((?:.(?!\/docs-nav-card))*)<\/docs-nav-card>/s; +const titleRule = /title="([^"]*)"/; +const hrefRule = /href="([^"]*)"/; +const iconImgSrcRule = /iconImgSrc="([^"]*)"/; + +export const docsNavCardExtension = { + name: 'docs-nav-card', + level: 'block' as const, + start(src: string) { + return src.match(/^\s* +
+ ${icon} +
${token.title}
+
+
+ +
+
+ `; + }, +}; + +interface DocsNavLinkToken extends Tokens.Generic { + type: 'docs-nav-link'; + title: string; + body: string; + href: string; + iconImgSrc?: string; // Need image since icons are custom + bodyTokens: Token[]; +} + +// Capture group 1: all attributes on the opening tag +// Capture group 2: all content between the open and close tags +const navLinkRule = /^[^<]*]*)>((?:.(?!\/docs-nav-link))*)<\/docs-nav-link>/s; + +export const docsNavLinkExtension = { + name: 'docs-nav-link', + level: 'block' as const, + start(src: string) { + src.match(/^\s* +
+ + ${this.parser.parse(token.bodyTokens)} +
+
+ `; + }, +}; diff --git a/adev/shared-docs/pipeline/shared/marked/extensions/docs-decorative-header.mts b/adev/shared-docs/pipeline/shared/marked/extensions/docs-decorative-header.mts index 3bc10e3991f..5695f9acb08 100644 --- a/adev/shared-docs/pipeline/shared/marked/extensions/docs-decorative-header.mts +++ b/adev/shared-docs/pipeline/shared/marked/extensions/docs-decorative-header.mts @@ -15,6 +15,7 @@ interface DocsDecorativeHeaderToken extends Tokens.Generic { type: 'docs-decorative-header'; title: string; imgSrc: string; + gradientBackground?: boolean; body: string; } @@ -25,6 +26,7 @@ const decorativeHeaderRule = const imgSrcRule = /imgSrc="([^"]*)"/; const titleRule = /title="([^"]*)"/; +const gradientBackgroundRule = /gradientBackground="([^"]*)"/; export const docsDecorativeHeaderExtension = { name: 'docs-decorative-header', @@ -41,12 +43,14 @@ export const docsDecorativeHeaderExtension = { const imgSrc = imgSrcRule.exec(attr); const title = titleRule.exec(attr); + const gradientBackground = gradientBackgroundRule.exec(attr); const token: DocsDecorativeHeaderToken = { type: 'docs-decorative-header', raw: match[0], title: title ? title[1] : '', imgSrc: imgSrc ? imgSrc[1] : '', + gradientBackground: gradientBackground ? gradientBackground[1] === 'true' : false, body: body ?? '', }; return token; @@ -54,26 +58,52 @@ export const docsDecorativeHeaderExtension = { return undefined; }, renderer(this: RendererThis, token: DocsDecorativeHeaderToken) { - // We can assume that all illustrations are svg files - // We need to read svg content, instead of renering svg with `img`, - // cause we would like to use CSS variables to support dark and light mode. - const illustration = token.imgSrc ? loadWorkspaceRelativeFile(token.imgSrc) : ''; - - return ` -
-
-
- - - ${getPageTitle(token.title, (this.parser.renderer as AdevDocsRenderer).context.markdownFilePath)} - -

${token.body}

-
- - - ${illustration} -
-
- `; + return token.gradientBackground + ? getGradientDecorativeHeader(this, token) + : getStandardDecorativeHeader(this, token); }, }; + +function getStandardDecorativeHeader(renderer: RendererThis, token: DocsDecorativeHeaderToken) { + // We can assume that all illustrations are svg files + // We need to read svg content, instead of renering svg with `img`, + // cause we would like to use CSS variables to support dark and light mode. + const illustration = token.imgSrc ? loadWorkspaceRelativeFile(token.imgSrc) : ''; + + return ` +
+
+
+ + + ${getPageTitle(token.title)} + +

${token.body}

+
+ + + ${illustration} +
+
+ `; +} + +function getGradientDecorativeHeader(renderer: RendererThis, token: DocsDecorativeHeaderToken) { + // We can assume that all illustrations are svg files + // We need to read svg content, instead of renering svg with `img`, + // cause we would like to use CSS variables to support dark and light mode. + const illustration = token.imgSrc ? loadWorkspaceRelativeFile(token.imgSrc) : ''; + + return ` +
+
+
+ ${getPageTitle(token.title)} +

${token.body}

+
+ + ${illustration} +
+
+ `; +} diff --git a/adev/shared-docs/pipeline/shared/marked/parse.mts b/adev/shared-docs/pipeline/shared/marked/parse.mts index e9119e0850e..d16a3c3a6d5 100644 --- a/adev/shared-docs/pipeline/shared/marked/parse.mts +++ b/adev/shared-docs/pipeline/shared/marked/parse.mts @@ -15,6 +15,7 @@ import {docsPillRowExtension} from './extensions/docs-pill/docs-pill-row.mjs'; import {docsVideoExtension} from './extensions/docs-video.mjs'; import {docsWorkflowExtension} from './extensions/docs-workflow/docs-workflow.mjs'; import {docsStepExtension} from './extensions/docs-workflow/docs-step.mjs'; +import {docsNavCardExtension, docsNavLinkExtension} from './extensions/docs-card/docs-nav-card.mjs'; import {docsCardExtension} from './extensions/docs-card/docs-card.mjs'; import {docsCardContainerExtension} from './extensions/docs-card/docs-card-container.mjs'; import {docsDecorativeHeaderExtension} from './extensions/docs-decorative-header.mjs'; @@ -39,6 +40,8 @@ const extensions = [ docsCodeBlockExtension, docsCodeExtension, docsCodeMultifileExtension, + docsNavCardExtension, + docsNavLinkExtension, docsTabExtension, docsTabGroupExtension, ]; diff --git a/adev/shared-docs/pipeline/shared/marked/test/docs-card-container/docs-card-container.md b/adev/shared-docs/pipeline/shared/marked/test/docs-card-container/docs-card-container.md index 6e74ebaaa6f..29511a71095 100644 --- a/adev/shared-docs/pipeline/shared/marked/test/docs-card-container/docs-card-container.md +++ b/adev/shared-docs/pipeline/shared/marked/test/docs-card-container/docs-card-container.md @@ -1,4 +1,4 @@ - - + + diff --git a/adev/shared-docs/styles/_colors.scss b/adev/shared-docs/styles/_colors.scss index bd650b061d4..8a0f7669d7b 100644 --- a/adev/shared-docs/styles/_colors.scss +++ b/adev/shared-docs/styles/_colors.scss @@ -11,10 +11,12 @@ --bright-blue: oklch(51.01% 0.274 263.83); // #0546ff --indigo-blue: oklch(51.64% 0.229 281.65); // #5c44e4 --electric-violet: oklch(53.18% 0.28 296.97); // #8514f5 + --electric-violet-header: oklch(53.18% 0.28 296.97); // #8514f5 --french-violet: oklch(47.66% 0.246 305.88); // #8001c6 --vivid-pink: oklch(69.02% 0.277 332.77); // #f637e3 --always-pink: oklch(69.02% 0.277 332.77); // #f637e3 --hot-pink: oklch(59.91% 0.239 8.14); // #e90464 + --hot-pink-header: oklch(59.91% 0.239 8.14); // #e90464 --hot-red: oklch(61.42% 0.238 15.34); // #f11653 --orange-red: oklch(63.32% 0.24 31.68); // #fa2c04 --super-green: oklch(79.12% 0.257 155.13); // #00c572 // Used for success, merge additions, etc. @@ -119,6 +121,24 @@ var(--vivid-pink) 100% ); + --pink-to-white-horizontal-gradient: linear-gradient( + 90deg, + var(--vivid-pink) 0%, + var(--gray-200) 60% + ); + + --light-pink-to-light-purple-horizontal-gradient: linear-gradient( + 90deg, + var(--light-pink) 0%, + var(--light-purple) 100% + ); + + --light-vivid-pink-to-light-electric-violet-horizontal-gradient: linear-gradient( + 90deg, + color-mix(in srgb, var(--vivid-pink), white 90%), + color-mix(in srgb, var(--electric-violet), white 90%) + ); + --purple-to-light-purple-vertical-gradient: linear-gradient( 0deg, var(--french-violet) 0%, @@ -156,6 +176,17 @@ var(--light-orange) 100% ); + --white-to-light-blue-diagonal-gradient: linear-gradient( + 132.83deg, + hsl(from color-mix(in srgb, var(--bright-blue), white 60%) h s l / 0) 45.38%, + hsl(from color-mix(in srgb, var(--bright-blue), white 60%) h s l / 0.3) 159.03%); + + --white-to-light-pink-diagonal-gradient: linear-gradient( + 135deg, + white 30%, + var(--light-pink) 100% + ); + // Radial Gradients --page-bg-radial-gradient: radial-gradient(circle, white 0%, white 100%); --soft-pink-radial-gradient: radial-gradient( @@ -164,6 +195,12 @@ white 80% ); + --hot-pink-to-electric-violet-radial-gradient: radial-gradient( + circle at 60% bottom, + var(--hot-pink-header) 20%, + var(--electric-violet-header) 100% + ); + // ABSTRACTIONS light - dark // --full-contrast: black - white // --primary-constrast: gray-900 - gray-100 @@ -193,6 +230,8 @@ // for the "unfilled" portion of the word that hasn't // been highlighted by the gradient --gray-unfilled: var(--gray-400); + --webgl-page-background: var(var(--page-background)) + --webgl-gray-unfilled: var(--gray-400); } @mixin dark-mode-definitions() { @@ -211,13 +250,20 @@ --bright-blue: color-mix(in srgb, oklch(51.01% 0.274 263.83), var(--full-contrast) 60%); --indigo-blue: color-mix(in srgb, oklch(51.64% 0.229 281.65), var(--full-contrast) 70%); --electric-violet: color-mix(in srgb, oklch(53.18% 0.28 296.97), var(--full-contrast) 70%); + --electric-violet-header: color-mix(in srgb, oklch(53.18% 0.28 296.97), var(--full-contrast) 15%); --french-violet: color-mix(in srgb, oklch(47.66% 0.246 305.88), var(--full-contrast) 70%); --vivid-pink: color-mix(in srgb, oklch(69.02% 0.277 332.77), var(--full-contrast) 70%); // --always-pink we keep the same in light mode --hot-pink: color-mix(in srgb, oklch(59.91% 0.239 8.14), var(--full-contrast) 70%); + --hot-pink-header: color-mix(in srgb, oklch(59.91% 0.239 8.14), var(--full-contrast) 15%); --hot-red: color-mix(in srgb, oklch(61.42% 0.238 15.34), var(--full-contrast) 70%); --orange-red: color-mix(in srgb, oklch(63.32% 0.24 31.68), var(--full-contrast) 60%); --super-green: color-mix(in srgb, oklch(79.12% 0.257 155.13), var(--full-contrast) 70%); + --light-vivid-pink-to-light-electric-violet-horizontal-gradient: + linear-gradient(hsl(from var(--gray-1000) h s l / 0.9), hsl(from var(--gray-1000) h s l / 0.9)), + linear-gradient(hsl(from var(--full-contrast) h s l / 0.2), hsl(from var(--full-contrast) h s l / 0.2)), + var(--pink-to-purple-horizontal-gradient); + --white-to-light-pink-diagonal-gradient: color-mix(in srgb, var(--french-violet), var(--full-contrast) 60%); --deprecated-dark: oklch(30% 0.02 304.04); // Base color --deprecated-docs-bg: var(--deprecated-dark); // Alias specifically for deprecated docs @@ -255,14 +301,23 @@ ); --page-bg-radial-gradient: radial-gradient(circle, black 0%, black 100%); - --soft-pink-radial-gradient: radial-gradient( - circle at center bottom, - var(--light-pink) 0%, - color-mix(in srgb, black, transparent 15%) 80% + --soft-pink-radial-gradient: + linear-gradient(119.77deg, + hsl(from color-mix(in srgb, var(--french-violet), white 60%) h s l / 0) 24.32%, + hsl(from color-mix(in srgb, var(--french-violet), white 60%) h s l / 0.3) 116.25%); + + // Use light values hot pink and electric violet since this gradient used for + // the decorative header which looks the same in both modes + --hot-pink-to-electric-violet-radial-gradient: radial-gradient( + circle at 60% bottom, + var(--hot-pink-header) 20%, + var(--electric-violet-header) 100% ); // Home page - dark mode --gray-unfilled: var(--gray-700); + --webgl-page-background: var(--gray-1000); + --webgl-gray-unfilled: var(--gray-700); .docs-toggle { input { diff --git a/adev/shared-docs/styles/docs/_card.scss b/adev/shared-docs/styles/docs/_card.scss index 44b364025a8..a844e7b3dc6 100644 --- a/adev/shared-docs/styles/docs/_card.scss +++ b/adev/shared-docs/styles/docs/_card.scss @@ -1,6 +1,102 @@ // Card Grid @mixin docs-card() { + .docs-card-container-wrapper { + border: 1px solid var(--senary-contrast); + border-radius: 0.25rem; + margin: 1rem 0; + + .docs-card-container-header { + align-items: flex-end; + border-bottom: 1px solid var(--senary-contrast); + display: flex; + justify-content: space-between; + + h2 { + padding: 2.5rem 1rem 2.5rem 2.5rem; + min-width: 350px; + } + + .theme-fill-bg { + fill: var(--page-background); + stroke: var(--primary-contrast); + } + .theme-fill-accent-bg { + fill: var(--septenary-contrast); + stroke: var(--primary-contrast); + } + .theme-stroke-primary { + stroke: var(--primary-contrast); + } + .theme-fill-primary { + fill: var(--primary-contrast); + } + .theme-fill-secondary { + fill: var(--octonary-contrast); + } + .theme-stroke-accent { + stroke: var(--vivid-pink); + } + .theme-fill-accent { + fill: var(--vivid-pink); + stroke: var(--primary-contrast); + } + .theme-stroke-secondary { + stroke: var(--senary-contrast); + } + .theme-fill-secondary { + fill: var(--senary-contrast); + } + + + + .header-img { + margin-top: 5px; + overflow: hidden; + margin-bottom: -5px; + } + + svg { + fill-opacity: 1; + + @container header (max-width: 550px) { + display: none; + } + + rect { + fill: transparent; + } + } + } + + .docs-card-container-content { + margin: 1rem; + padding: 1.5rem; + + .docs-card { + margin: 0; + + svg { + margin-block-end: 0.75rem; + } + } + } + } + + .docs-card-container-wrapper:nth-child(odd) { + background-image: var(--white-to-light-blue-diagonal-gradient); + .docs-card-container-header { + background-image: var(--white-to-light-blue-diagonal-gradient); + } + } + + .docs-card-container-wrapper:nth-child(even) { + background-image: var(--white-to-light-pink-diagonal-gradient); + .docs-card-container-header { + background-image: var(--white-to-light-pink-diagonal-gradient); + } + } + .docs-card-grid { display: grid; grid-template-columns: repeat(2, 1fr); @@ -12,6 +108,10 @@ } } + :not(.docs-card-container) .docs-card { + margin: 1rem 0; + } + .docs-card { display: flex; flex-direction: column; @@ -101,4 +201,80 @@ margin-block-start: 0; font-size: 1rem; } + + .docs-nav-card { + position: relative; + background: var(--light-vivid-pink-to-light-electric-violet-horizontal-gradient); + container: header / inline-size; + background-clip: padding-box; + border: solid 1px transparent; + border-radius: 0.25rem; + margin: 1rem 0; + + &:before { + content: ''; + position: absolute; + top: 0; right: 0; bottom: 0; left: 0; + z-index: -1; + margin: -1px; + border-radius: inherit; + background: var(--pink-to-white-horizontal-gradient); + } + + .docs-nav-card-title { + background: var(--pink-to-highlight-to-purple-to-blue-horizontal-gradient); + padding: 1.5rem 1.5rem .5rem; + -webkit-background-clip: text; + background-clip: text; + color: transparent; + display: flex; + align-items: baseline; + gap: 8px; + + h6 { + margin: 0; + } + } + + .docs-nav-card-content { + display: flex; + margin-block: 0; + position: relative; + overflow: hidden; + padding: 0 1.5rem 1.5rem; + } + + .docs-nav-card-links { + width: 100%; + } + + .docs-nav-card-svg { + width: 350px; + + @container header (max-width: 550px) { + display: none; + } + } + + a.docs-nav-link { + text-decoration: none; + + .docs-nav-link-title { + display: flex; + gap: 8px; + + h3 { + margin-block-end: .5rem; + } + + svg path { + fill: var(--gray-400); + } + } + + p { + margin-block: 0; + } + } + } } diff --git a/adev/shared-docs/styles/docs/_decorative-header.scss b/adev/shared-docs/styles/docs/_decorative-header.scss index 85467a7c475..8284ec78e20 100644 --- a/adev/shared-docs/styles/docs/_decorative-header.scss +++ b/adev/shared-docs/styles/docs/_decorative-header.scss @@ -15,60 +15,14 @@ flex-direction: column-reverse; } - .docs-header-content { - box-sizing: border-box; - padding: 1.5rem; - padding-inline-end: 0; - flex-grow: 1; - - @container header (max-width: 550px) { - width: 100%; - padding-block-end: 1.5rem; - } - - h1, - p, - span { - color: var(--primary-contrast); - transition: color 0.3s ease; - } - - a { - position: absolute; - top: 1.5rem; - right: 1.5rem; - z-index: 20; - i { - color: var(--quaternary-contrast); - } - - &:hover { - i { - color: var(--primary-contrast); - } - } - } - - docs-breadcrumb { - padding-block-end: 1rem; - } - - .docs-breadcrumb { - font-size: 0.875rem; - span { - color: var(--primary-contrast) !important; - } - } - } - svg { - margin: 0; margin-block: auto; - padding-inline: 1rem 3.5rem; - padding-block: 2rem; - min-width: 150px; - max-width: 250px; + margin: 0; max-height: 125px; + max-width: 250px; + min-width: 150px; + padding-block: 2rem; + padding-inline: 1rem 3.5rem; z-index: 0; &.docs-what-is-angular-svg { @@ -86,13 +40,108 @@ } @container header (max-width: 550px) { - padding: 2rem; + max-height: 125px; + max-width: 80%; + min-width: auto; padding-block-end: 0; padding-inline-start: 1.5rem; + padding: 2rem; width: fit-content; - min-width: auto; - max-width: 80%; - max-height: 125px; + } + } + } + + .docs-viewer:has(.docs-decorative-gradient-header) { + padding: 0 var(--layout-padding) var(--layout-padding); + } + + .docs-decorative-gradient-header { + background: var(--hot-pink-to-electric-violet-radial-gradient); + border-radius: 0 0 0.625rem 0.625rem; + display: flex; + max-width: var(--page-width); + overflow: hidden; + position: relative; + transition: background 0.3s ease; + + @container header (max-width: 550px) { + flex-direction: column-reverse; + } + + .docs-header-content { + padding: 5rem 12rem 5rem 2.5rem; + @container header (max-width: 550px) { + padding: 5rem 2.5rem; + } + + h1, + p { + color: white; + } + + .docs-breadcrumb span { + color: white !important; + } + } + + svg { + bottom: -50px; + position: absolute; + right: -75px; + z-index: 0; + + path { + stroke: var(--vivid-pink); + } + + @container header (max-width: 550px) { + display: none; + } + } + } + + .docs-header-content { + box-sizing: border-box; + flex-grow: 1; + padding-inline-end: 0; + padding: 1.5rem; + + @container header (max-width: 550px) { + padding-block-end: 1.5rem; + width: 100%; + } + + h1, + p, + span { + color: var(--primary-contrast); + transition: color 0.3s ease; + } + + a { + position: absolute; + right: 1.5rem; + top: 1.5rem; + z-index: 20; + i { + color: var(--quaternary-contrast); + } + + &:hover { + i { + color: var(--primary-contrast); + } + } + } + + docs-breadcrumb { + padding-block-end: 1rem; + } + + .docs-breadcrumb { + font-size: 0.875rem; + span { + color: var(--primary-contrast) !important; } } } diff --git a/adev/src/app/features/docs/docs.component.html b/adev/src/app/features/docs/docs.component.html index d768750db0f..a820c221487 100644 --- a/adev/src/app/features/docs/docs.component.html +++ b/adev/src/app/features/docs/docs.component.html @@ -2,6 +2,7 @@ @if (docContent) {
} diff --git a/adev/src/app/features/docs/docs.component.scss b/adev/src/app/features/docs/docs.component.scss index 45862baa07f..c63d60f60fe 100644 --- a/adev/src/app/features/docs/docs.component.scss +++ b/adev/src/app/features/docs/docs.component.scss @@ -16,6 +16,10 @@ display: flex; justify-content: center; } + + &.overview { + padding-top: 0; + } } @keyframes fade-in { diff --git a/adev/src/app/features/docs/docs.component.ts b/adev/src/app/features/docs/docs.component.ts index 61519b9816a..2e43761ea22 100644 --- a/adev/src/app/features/docs/docs.component.ts +++ b/adev/src/app/features/docs/docs.component.ts @@ -7,7 +7,7 @@ */ import {DocContent, DocViewer} from '@angular/docs'; -import {ChangeDetectionStrategy, Component, input} from '@angular/core'; +import {ChangeDetectionStrategy, Component, computed, input} from '@angular/core'; @Component({ selector: 'docs-docs', @@ -15,11 +15,15 @@ import {ChangeDetectionStrategy, Component, input} from '@angular/core'; imports: [DocViewer], styleUrls: ['./docs.component.scss'], templateUrl: './docs.component.html', + host: { + '[class.overview]': 'isOverview()', + }, }) export default class DocsComponent { // Based on current route, proper static content for doc page is fetched. // In case when exists example-viewer placeholders, then ExampleViewer // components are going to be rendered. - protected readonly docContent = input(); + docContent = input(); + isOverview = computed(() => this.docContent()?.id.includes('what-is-angular')); } diff --git a/adev/src/assets/icons/cli.svg b/adev/src/assets/icons/cli.svg new file mode 100644 index 00000000000..7ebfa409978 --- /dev/null +++ b/adev/src/assets/icons/cli.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/adev/src/assets/icons/components.svg b/adev/src/assets/icons/components.svg new file mode 100644 index 00000000000..57bbb1ee07d --- /dev/null +++ b/adev/src/assets/icons/components.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/adev/src/assets/icons/dev-tools.svg b/adev/src/assets/icons/dev-tools.svg new file mode 100644 index 00000000000..816fee78e62 --- /dev/null +++ b/adev/src/assets/icons/dev-tools.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/adev/src/assets/icons/di.svg b/adev/src/assets/icons/di.svg new file mode 100644 index 00000000000..4ef8ea3cfb5 --- /dev/null +++ b/adev/src/assets/icons/di.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/adev/src/assets/icons/docs.svg b/adev/src/assets/icons/docs.svg new file mode 100644 index 00000000000..70033be66de --- /dev/null +++ b/adev/src/assets/icons/docs.svg @@ -0,0 +1,3 @@ + + + diff --git a/adev/src/assets/icons/forms.svg b/adev/src/assets/icons/forms.svg new file mode 100644 index 00000000000..c8ebe971e51 --- /dev/null +++ b/adev/src/assets/icons/forms.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/adev/src/assets/icons/language-service.svg b/adev/src/assets/icons/language-service.svg new file mode 100644 index 00000000000..fbd371e45af --- /dev/null +++ b/adev/src/assets/icons/language-service.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/adev/src/assets/icons/ng-update.svg b/adev/src/assets/icons/ng-update.svg new file mode 100644 index 00000000000..4f0c7fc38de --- /dev/null +++ b/adev/src/assets/icons/ng-update.svg @@ -0,0 +1,4 @@ + + + + diff --git a/adev/src/assets/icons/routing.svg b/adev/src/assets/icons/routing.svg new file mode 100644 index 00000000000..cbe4cfb3a88 --- /dev/null +++ b/adev/src/assets/icons/routing.svg @@ -0,0 +1,4 @@ + + + + diff --git a/adev/src/assets/icons/signals.svg b/adev/src/assets/icons/signals.svg new file mode 100644 index 00000000000..65c9c1d27b7 --- /dev/null +++ b/adev/src/assets/icons/signals.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/adev/src/assets/icons/ssr.svg b/adev/src/assets/icons/ssr.svg new file mode 100644 index 00000000000..a6d96c6ce35 --- /dev/null +++ b/adev/src/assets/icons/ssr.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/adev/src/assets/icons/star.svg b/adev/src/assets/icons/star.svg new file mode 100644 index 00000000000..9327dfed3b9 --- /dev/null +++ b/adev/src/assets/icons/star.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/adev/src/assets/icons/tutorials.svg b/adev/src/assets/icons/tutorials.svg new file mode 100644 index 00000000000..40db0da861e --- /dev/null +++ b/adev/src/assets/icons/tutorials.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/adev/src/assets/images/browser-bolt.svg b/adev/src/assets/images/browser-bolt.svg new file mode 100644 index 00000000000..98ea5f3667a --- /dev/null +++ b/adev/src/assets/images/browser-bolt.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/adev/src/assets/images/community.svg b/adev/src/assets/images/community.svg new file mode 100644 index 00000000000..879a1ab9de6 --- /dev/null +++ b/adev/src/assets/images/community.svg @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/adev/src/assets/images/editor-dark-horizontal.svg b/adev/src/assets/images/editor-dark-horizontal.svg deleted file mode 100644 index d44a4a36661..00000000000 --- a/adev/src/assets/images/editor-dark-horizontal.svg +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/adev/src/assets/images/editor-dark-vertical.svg b/adev/src/assets/images/editor-dark-vertical.svg deleted file mode 100644 index 200756d96b9..00000000000 --- a/adev/src/assets/images/editor-dark-vertical.svg +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/adev/src/assets/images/editor-light-horizontal.svg b/adev/src/assets/images/editor-light-horizontal.svg deleted file mode 100644 index aaac9087648..00000000000 --- a/adev/src/assets/images/editor-light-horizontal.svg +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/adev/src/assets/images/editor-light-vertical.svg b/adev/src/assets/images/editor-light-vertical.svg deleted file mode 100644 index eb2f2451426..00000000000 --- a/adev/src/assets/images/editor-light-vertical.svg +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/adev/src/assets/images/learn-angular-browser-bw.svg b/adev/src/assets/images/learn-angular-browser-bw.svg new file mode 100644 index 00000000000..704470be611 --- /dev/null +++ b/adev/src/assets/images/learn-angular-browser-bw.svg @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/adev/src/assets/images/open-source.svg b/adev/src/assets/images/open-source.svg new file mode 100644 index 00000000000..d9ead79b788 --- /dev/null +++ b/adev/src/assets/images/open-source.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/adev/src/assets/images/rocket.svg b/adev/src/assets/images/rocket.svg new file mode 100644 index 00000000000..232d7e7729c --- /dev/null +++ b/adev/src/assets/images/rocket.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/adev/src/assets/images/servers.svg b/adev/src/assets/images/servers.svg new file mode 100644 index 00000000000..80e444ba1d8 --- /dev/null +++ b/adev/src/assets/images/servers.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/adev/src/content/introduction/BUILD.bazel b/adev/src/content/introduction/BUILD.bazel index d5b42264b02..3884defca06 100644 --- a/adev/src/content/introduction/BUILD.bazel +++ b/adev/src/content/introduction/BUILD.bazel @@ -8,6 +8,27 @@ generate_guides( ]), api_manifest = "//adev/src/assets:docs_api_manifest", data = [ + "//adev/src/assets/icons:cli.svg", + "//adev/src/assets/icons:components.svg", + "//adev/src/assets/icons:dev-tools.svg", + "//adev/src/assets/icons:di.svg", + "//adev/src/assets/icons:docs.svg", + "//adev/src/assets/icons:forms.svg", + "//adev/src/assets/icons:language-service.svg", + "//adev/src/assets/icons:ng-update.svg", + "//adev/src/assets/icons:routing.svg", + "//adev/src/assets/icons:signals.svg", + "//adev/src/assets/icons:ssr.svg", + "//adev/src/assets/icons:star.svg", + "//adev/src/assets/icons:tutorials.svg", + "//adev/src/assets/images:browser-bolt.svg", + "//adev/src/assets/images:community.svg", + "//adev/src/assets/images:globe.svg", + "//adev/src/assets/images:learn-angular-browser.svg", + "//adev/src/assets/images:learn-angular-browser-bw.svg", + "//adev/src/assets/images:open-source.svg", + "//adev/src/assets/images:rocket.svg", + "//adev/src/assets/images:servers.svg", "//adev/src/assets/images:what_is_angular.svg", ], visibility = ["//adev:__subpackages__"], diff --git a/adev/src/content/introduction/what-is-angular.md b/adev/src/content/introduction/what-is-angular.md index d48013db162..8fa5ebe4a0b 100644 --- a/adev/src/content/introduction/what-is-angular.md +++ b/adev/src/content/introduction/what-is-angular.md @@ -1,144 +1,115 @@ - + + Angular is a web framework that empowers
+ developers to build fast, reliable applications that users love.
-

-Angular is a web framework that empowers developers to build fast, reliable applications. -

+
+ Maintained by a dedicated team at Google, Angular provides a broad suite of tools, APIs, and + libraries to simplify and streamline your development workflow. Angular gives you + a solid platform on which to build fast, reliable applications that scale with both the size of + your team and the size of your codebase. +
-Maintained by a dedicated team at Google, Angular provides a broad suite of tools, APIs, and -libraries to simplify and streamline your development workflow. Angular gives you -a solid platform on which to build fast, reliable applications that scale with both the size of -your team and the size of your codebase. + + + An overview of what it's like to use Angular + + + Step-by-step instructions in your browser + + -**Want to see some code?** Jump over to our [Essentials](essentials) for a quick overview of -what it's like to use Angular, or get started in the [Tutorial](tutorials/learn-angular) if you -prefer following step-by-step instructions. - -## Features that power your development - - - - Angular components make it easy to split your code into well-encapsulated parts. - -The versatile dependency injection helps you keep your code modular, loosely-coupled, and -testable. - - -Our fine-grained reactivity model, combined with compile-time optimizations, simplifies development and helps build faster apps by default. - -Granularly track how and where state is used throughout an application, giving the framework the power to render fast updates via highly optimized instructions. - - -Angular supports both server-side rendering (SSR) and static site generation (SSG) along -with full DOM hydration. `@defer` blocks in templates make it simple to declaratively divide -your templates into lazy-loadable parts. - - -[Angular's router](guide/routing) provides a feature-rich navigation toolkit, including support -for route guards, data resolution, lazy-loading, and much more. - -[Angular's forms module](guide/forms) provides a standardized system for form participation and validation. - - - -## Develop applications faster than ever - - - - Angular CLI gets your project running in under a minute with the commands you need to - grow into a deployed production application. + + + Angular components make it easy to split your code into well-encapsulated parts. - - Angular DevTools sits alongside your browser's developer tools. It helps debug and analyze your - app, including a component tree inspector, dependency injection tree view, - and custom performance profiling flame chart. + + Our fine-grained reactivity model, combined with compile-time optimizations, simplifies development and helps build faster apps by default. - - Angular CLI's `ng update` runs automated code transformations that automatically handle routine - breaking changes, dramatically simplifying major version updates. Keeping up with the latest - version keeps your app as fast and secure as possible. + + Angular supports both server-side rendering (SSR) and static site generation (SSG) along with full DOM hydration. - - Angular's IDE language services powers code completion, navigation, refactoring, and real-time - diagnostics in your favorite editor. + + Easily share code across components throughout your entire application. + + + Provides a feature-rich navigation toolkit, including support for route guards, data resolution, lazy-loading, and much more. + + + Provides a standardized system for form participation and validation. -## Ship with confidence - - - - Every Angular commit is checked against _hundreds of thousands_ of tests in Google's internal code - repository, representing countless real-world scenarios. - -Angular is committed to stability for some of Google’s largest products, including Google Cloud. -This commitment ensures changes are well-tested, backwards compatible, and include migration tools -whenever possible. - - -Angular's predictable, time-based release schedule gives your organization confidence in the -stability and backwards compatibility of the framework. Long Term Support (LTS) windows make sure -you get critical security fixes when you need them. First-party update tools, guides and automated -migration schematics help keep your apps up-to-date with the latest advancements to the framework -and the web platform. - - - -## Works at any scale - - - - Angular's internationalization features handle message translations and formatting, including - support for unicode standard ICU syntax. + + + Angular CLI gets your project running in under a minute with the commands you need to grow into a deployed production application. - - In collaboration with Google's world-class security engineers, Angular aims to make development - safe by default. Built-in security features, including HTML sanitization and - trusted type support, help protect your users from common vulnerabilities like - cross-site scripting and cross-site request forgery. + + Angular DevTools sits alongside your browser's developer tools. It helps debug and analyze your app, including a component tree inspector, dependency injection tree view, and custom performance profiling flame chart. - - Angular CLI includes a fast, modern build pipeline using Vite and ESBuild. Developers report - building projects with hundreds of thousands of lines of code in less than a minute. + + Angular CLI's `ng update` runs automated code transformations that automatically handle routine breaking changes, dramatically simplifying major version updates. Keeping up with the latest version keeps your app as fast and secure as possible. - - Large Google products build on top of Angular's architecture and help develop new features that - further improve Angular's scalability, from [Google Fonts](https://fonts.google.com/) to [Google Cloud](https://console.cloud.google.com). + + Angular's IDE language services powers code completion, navigation, refactoring, and real-time diagnostics in your favorite editor. -## Open-source first - - - - Curious what we’re working on? Every PR and commit is available on our GitHub. Run into an issue or bug? We triage GitHub issues regularly to ensure we’re responsive and engaged with our community, and solving the real world problems you’re facing. + + +

Every Angular commit is checked against hundreds of thousands of tests in Google's internal code repository, representing countless real-world scenarios.

+

Angular is committed to stability for some of Google’s largest products, including Google Cloud. This commitment ensures changes are well-tested, backwards compatible, and include migration tools whenever possible.

- - Our team publishes a public roadmap of our current and future work and values your feedback. We publish Request for Comments (RFCs) to collect feedback on larger feature changes and ensure the community voice is heard while shaping the future direction of Angular. + + Angular's predictable, time-based release schedule gives your organization confidence in the stability and backwards compatibility of the framework. Long Term Support (LTS) windows make sure you get critical security fixes when you need them. First-party update tools, guides and automated migration schematics help keep your apps up-to-date with the latest advancements to the framework and the web platform.
-## A thriving community - - - - Our community is composed of talented developers, writers, instructors, podcasters, and more. The Google for Developers library is just a sample of the high quality resources available for new and experienced developers to continue developing. + + + Angular's internationalization features handle message translations and formatting, including + support for unicode standard ICU syntax. - - We are thankful for the open source contributors who make Angular a better framework for everyone. From fixing a typo in the docs, to adding major features, we encourage anyone interested to get started on our GitHub. + + In collaboration with Google's world-class security engineers, Angular aims to make development + safe by default. Built-in security features, including HTML sanitization and + trusted type support, help protect your users from common vulnerabilities like + cross-site scripting and cross-site request forgery. - - Our team partners with individuals, educators, and enterprises to ensure we consistently are supporting developers. Angular Google Developer Experts (GDEs) represent community leaders around the world educating, organizing, and developing with Angular. Enterprise partnerships help ensure that Angular scales well for technology industry leaders. + + Angular CLI includes a fast, modern build pipeline using Vite and ESBuild. Developers report + building projects with hundreds of thousands of lines of code in less than a minute. - - Angular partners closely with other Google technologies and teams to improve the web. + + Large Google products build on top of Angular's architecture and help develop new features that + further improve Angular's scalability, from [Google Fonts](https://fonts.google.com/) to [Google Cloud](https://console.cloud.google.com). + + -Our ongoing partnership with Chrome’s Aurora actively explores improvements to user experience across the web, developing built-in performance optimizations like NgOptimizedImage and improvements to Angular’s Core Web Vitals. + + + Curious what we’re working on? Every PR and commit is available on our GitHub. Run into an issue or bug? We triage GitHub issues regularly to ensure we’re responsive and engaged with our community, and solving the real world problems you’re facing. + + + Our team publishes a public roadmap of our current and future work and values your feedback. We publish Request for Comments (RFCs) to collect feedback on larger feature changes and ensure the community voice is heard while shaping the future direction of Angular. + + -We are also working with [Firebase](https://firebase.google.com/), [Tensorflow](https://www.tensorflow.org/), [Flutter](https://flutter.dev/), [Material Design](https://m3.material.io/), and [Google Cloud](https://cloud.google.com/) to ensure we provide meaningful integrations across the developer workflow. - + + + Our community is composed of talented developers, writers, instructors, podcasters, and more. The Google for Developers library is just a sample of the high quality resources available for new and experienced developers to continue developing. + + + We are thankful for the open source contributors who make Angular a better framework for everyone. From fixing a typo in the docs, to adding major features, we encourage anyone interested to get started on our GitHub. + + + Our team partners with individuals, educators, and enterprises to ensure we consistently are supporting developers. Angular Google Developer Experts (GDEs) represent community leaders around the world educating, organizing, and developing with Angular. Enterprise partnerships help ensure that Angular scales well for technology industry leaders. + + +

Angular partners closely with other Google technologies and teams to improve the web.

+

Our ongoing partnership with Chrome’s Aurora actively explores improvements to user experience across the web, developing built-in performance optimizations like NgOptimizedImage and improvements to Angular’s Core Web Vitals.

+

We are also working with Firebase, Tensorflow, Flutter, Material Design, and Google Cloud to ensure we provide meaningful integrations across the developer workflow.

+