@rblabs-dev/axiom 1.2.4 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -175,7 +175,7 @@ const RB_MESSAGE_TONE_LABELS = {
175
175
  warning: "Warning",
176
176
  danger: "Error"
177
177
  };
178
- const RB_DESIGN_SYSTEM_VERSION = "1.2.4";
178
+ const RB_DESIGN_SYSTEM_VERSION = "1.3.0";
179
179
  export { RB_BREAKPOINTS, RB_BREAKPOINT_WIDTHS, RB_DESIGN_SYSTEM_VERSION, RB_ICON_FONT_SET, RB_MESSAGE_TONE_LABELS, RB_THEME_COLORS, RB_THEME_OPTIONS, RB_THEME_STORAGE_KEY, RB_TONE_ICONS, ThemeService, provideRbDesignSystem };
180
180
 
181
181
  //# sourceMappingURL=rblabs-dev-axiom-core.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"rblabs-dev-axiom-core.mjs","names":[],"sources":["../../../projects/axiom/core/src/breakpoints.ts","../../../projects/axiom/core/src/theme.service.ts","../../../projects/axiom/core/src/provide-design-system.ts","../../../projects/axiom/core/src/tones.ts","../../../projects/axiom/core/src/version.ts"],"sourcesContent":["/** Breakpoint widths (docs/design-system.md, section 8). Mirrors `$breakpoints` in _breakpoints.scss. */\nexport const RB_BREAKPOINT_WIDTHS = {\n xs: '30rem',\n sm: '40rem',\n md: '48rem',\n lg: '60rem',\n xl: '75rem',\n '2xl': '87.5rem',\n} as const;\n\nexport type RbBreakpoint = keyof typeof RB_BREAKPOINT_WIDTHS;\n\n/** Media queries for the CDK `BreakpointObserver`, e.g. `observe(RB_BREAKPOINTS.belowLg)`. */\nexport const RB_BREAKPOINTS = {\n belowXs: '(width < 30rem)',\n belowSm: '(width < 40rem)',\n belowMd: '(width < 48rem)',\n belowLg: '(width < 60rem)',\n belowXl: '(width < 75rem)',\n below2xl: '(width < 87.5rem)',\n aboveXs: '(width >= 30rem)',\n aboveSm: '(width >= 40rem)',\n aboveMd: '(width >= 48rem)',\n aboveLg: '(width >= 60rem)',\n aboveXl: '(width >= 75rem)',\n above2xl: '(width >= 87.5rem)',\n} as const;\n","import { DOCUMENT } from '@angular/common';\nimport { DestroyRef, Injectable, InjectionToken, computed, inject, signal } from '@angular/core';\n\nexport type RbTheme = 'light' | 'dark';\n\n/** localStorage key holding the user's explicit choice (docs/design-system.md, section 11). */\nexport const RB_THEME_STORAGE_KEY = 'rb-theme';\n\n/** `--rb-color-bg` per theme; also the `<meta name=\"theme-color\">` value. */\nexport const RB_THEME_COLORS: Readonly<Record<RbTheme, string>> = {\n light: '#f4f7fb',\n dark: '#08111d',\n};\n\nexport interface RbThemeOptions {\n /**\n * Forces a theme regardless of the stored choice and the OS, without persisting anything. Used by\n * apps that only support one theme until their migration is done. The theme toggle hides itself\n * while a theme is forced.\n */\n forcedTheme?: RbTheme | null;\n /** Storage keys an app used before adopting the kit; a valid value is moved to `rb-theme` once. */\n legacyStorageKeys?: readonly string[];\n}\n\nexport const RB_THEME_OPTIONS = new InjectionToken<RbThemeOptions>('RB_THEME_OPTIONS', {\n providedIn: 'root',\n factory: () => ({}),\n});\n\nconst DARK_SCHEME_QUERY = '(prefers-color-scheme: dark)';\n\nfunction isTheme(value: unknown): value is RbTheme {\n return value === 'light' || value === 'dark';\n}\n\n/**\n * Owns the light/dark theme contract (docs/design-system.md, section 11):\n *\n * - an explicit choice is stored under `rb-theme` and wins over the OS;\n * - without one, the theme follows `prefers-color-scheme` and tracks OS changes live;\n * - the resolved theme is written to `<html>` as `data-theme`, and every\n * `<meta name=\"theme-color\">` is kept in sync;\n * - `color-scheme` is only set inline for an explicit or forced theme; otherwise the stylesheet's\n * `color-scheme: light dark` already follows the OS before any script runs.\n *\n * `provideRbDesignSystem()` calls `initialize()` from an app initializer, before first paint.\n */\n@Injectable({ providedIn: 'root' })\nexport class ThemeService {\n private readonly document = inject(DOCUMENT);\n private readonly options = inject(RB_THEME_OPTIONS);\n private readonly window = this.document.defaultView;\n private readonly darkScheme = this.window?.matchMedia?.(DARK_SCHEME_QUERY) ?? null;\n\n private readonly systemTheme = signal<RbTheme>(this.darkScheme?.matches ? 'dark' : 'light');\n private readonly storedTheme = signal<RbTheme | null>(null);\n private readonly forcedTheme = signal<RbTheme | null>(this.options.forcedTheme ?? null);\n private initialized = false;\n\n /** The user's explicit choice, or `null` while the theme follows the OS. */\n readonly preference = this.storedTheme.asReadonly();\n /** The theme in effect: forced, then stored, then the OS preference. */\n readonly theme = computed<RbTheme>(\n () => this.forcedTheme() ?? this.storedTheme() ?? this.systemTheme(),\n );\n readonly isDark = computed(() => this.theme() === 'dark');\n /** True while a theme is forced; the toggle is hidden then. */\n readonly isForced = computed(() => this.forcedTheme() !== null);\n\n constructor() {\n const onSystemChange = (event: MediaQueryListEvent): void => {\n this.systemTheme.set(event.matches ? 'dark' : 'light');\n this.apply();\n };\n this.darkScheme?.addEventListener('change', onSystemChange);\n inject(DestroyRef).onDestroy(() =>\n this.darkScheme?.removeEventListener('change', onSystemChange),\n );\n }\n\n /** Reads the stored choice and applies the theme to the document. Safe to call more than once. */\n initialize(): void {\n if (!this.initialized) {\n this.initialized = true;\n this.storedTheme.set(this.readStoredTheme());\n }\n this.apply();\n }\n\n /** Switches between light and dark and stores the result as an explicit choice. */\n toggle(): void {\n this.setTheme(this.theme() === 'dark' ? 'light' : 'dark');\n }\n\n /** Stores an explicit choice; OS changes are ignored from now on. */\n setTheme(theme: RbTheme): void {\n this.initialized = true;\n this.storedTheme.set(theme);\n this.write(RB_THEME_STORAGE_KEY, theme);\n this.apply();\n }\n\n /** Forgets the explicit choice, so the theme follows the OS again. */\n clearPreference(): void {\n this.initialized = true;\n this.storedTheme.set(null);\n this.remove(RB_THEME_STORAGE_KEY);\n this.apply();\n }\n\n /** Forces a theme (or releases it with `null`) without touching the stored choice. */\n forceTheme(theme: RbTheme | null): void {\n this.forcedTheme.set(theme);\n this.apply();\n }\n\n private apply(): void {\n const root = this.document.documentElement;\n const theme = this.theme();\n const explicit = this.forcedTheme() ?? this.storedTheme();\n\n root.dataset['theme'] = theme;\n if (explicit) {\n root.style.colorScheme = explicit;\n } else {\n root.style.removeProperty('color-scheme');\n }\n\n for (const meta of Array.from(\n this.document.querySelectorAll<HTMLMetaElement>('meta[name=\"theme-color\"]'),\n )) {\n meta.content = RB_THEME_COLORS[theme];\n }\n }\n\n private readStoredTheme(): RbTheme | null {\n const stored = this.read(RB_THEME_STORAGE_KEY);\n let theme: RbTheme | null = isTheme(stored) ? stored : null;\n\n for (const legacyKey of this.options.legacyStorageKeys ?? []) {\n const legacy = this.read(legacyKey);\n if (theme === null && isTheme(legacy)) {\n theme = legacy;\n this.write(RB_THEME_STORAGE_KEY, legacy);\n }\n if (legacy !== null) this.remove(legacyKey);\n }\n\n return theme;\n }\n\n // Storage can be unavailable (privacy modes, disabled storage); the theme then follows the OS.\n private read(key: string): string | null {\n try {\n return this.window?.localStorage.getItem(key) ?? null;\n } catch {\n return null;\n }\n }\n\n private write(key: string, value: string): void {\n try {\n this.window?.localStorage.setItem(key, value);\n } catch {\n // Ignore: the choice still applies for this session.\n }\n }\n\n private remove(key: string): void {\n try {\n this.window?.localStorage.removeItem(key);\n } catch {\n // Ignore: nothing to clean up without storage.\n }\n }\n}\n","import {\n EnvironmentProviders,\n inject,\n makeEnvironmentProviders,\n provideAppInitializer,\n} from '@angular/core';\nimport { MatIconRegistry } from '@angular/material/icon';\n\nimport { RB_THEME_OPTIONS, RbThemeOptions, ThemeService } from './theme.service';\n\n/** Font set class of the self-hosted Material Symbols subset (docs/design-system.md, section 10). */\nexport const RB_ICON_FONT_SET = 'material-symbols-outlined';\n\nexport type RbDesignSystemOptions = RbThemeOptions;\n\n/**\n * Registers the kit's runtime pieces in `app.config.ts`:\n *\n * - applies the theme before the root component renders (no flash for a stored choice);\n * - makes `material-symbols-outlined` the default `<mat-icon>` font set.\n */\nexport function provideRbDesignSystem(options: RbDesignSystemOptions = {}): EnvironmentProviders {\n return makeEnvironmentProviders([\n { provide: RB_THEME_OPTIONS, useValue: options },\n provideAppInitializer(() => {\n inject(ThemeService).initialize();\n inject(MatIconRegistry).setDefaultFontSetClass(RB_ICON_FONT_SET);\n }),\n ]);\n}\n","/** Tones of `rb-message` (docs/design-system.md, section 15.3). */\nexport type RbMessageTone = 'neutral' | 'info' | 'success' | 'warning' | 'danger';\n\n/** Tones of `rb-status-chip` and page header metrics (15.1, 15.5). `active` means in progress. */\nexport type RbStatusTone = 'neutral' | 'active' | 'success' | 'warning' | 'danger';\n\n/**\n * Default icon per tone, so state is never carried by color alone (section 9.4). Neutral status\n * chips and metrics have no default icon: their label is the whole message.\n */\nexport const RB_TONE_ICONS: Readonly<Record<RbMessageTone | RbStatusTone, string | null>> = {\n neutral: null,\n info: 'info',\n active: 'autorenew',\n success: 'check_circle',\n warning: 'warning',\n danger: 'error',\n};\n\n/** Visually hidden prefix that names a message's tone for screen reader users. */\nexport const RB_MESSAGE_TONE_LABELS: Readonly<Record<RbMessageTone, string | null>> = {\n neutral: null,\n info: 'Information',\n success: 'Success',\n warning: 'Warning',\n danger: 'Error',\n};\n","/**\n * Kit version. The build stamps it from projects/axiom/package.json (scripts/stamp-version.mjs);\n * source and development builds show the placeholder.\n */\nexport const RB_DESIGN_SYSTEM_VERSION: string = '1.2.4';\n"],"mappings":"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"}
1
+ {"version":3,"file":"rblabs-dev-axiom-core.mjs","names":[],"sources":["../../../projects/axiom/core/src/breakpoints.ts","../../../projects/axiom/core/src/theme.service.ts","../../../projects/axiom/core/src/provide-design-system.ts","../../../projects/axiom/core/src/tones.ts","../../../projects/axiom/core/src/version.ts"],"sourcesContent":["/** Breakpoint widths (docs/design-system.md, section 8). Mirrors `$breakpoints` in _breakpoints.scss. */\nexport const RB_BREAKPOINT_WIDTHS = {\n xs: '30rem',\n sm: '40rem',\n md: '48rem',\n lg: '60rem',\n xl: '75rem',\n '2xl': '87.5rem',\n} as const;\n\nexport type RbBreakpoint = keyof typeof RB_BREAKPOINT_WIDTHS;\n\n/** Media queries for the CDK `BreakpointObserver`, e.g. `observe(RB_BREAKPOINTS.belowLg)`. */\nexport const RB_BREAKPOINTS = {\n belowXs: '(width < 30rem)',\n belowSm: '(width < 40rem)',\n belowMd: '(width < 48rem)',\n belowLg: '(width < 60rem)',\n belowXl: '(width < 75rem)',\n below2xl: '(width < 87.5rem)',\n aboveXs: '(width >= 30rem)',\n aboveSm: '(width >= 40rem)',\n aboveMd: '(width >= 48rem)',\n aboveLg: '(width >= 60rem)',\n aboveXl: '(width >= 75rem)',\n above2xl: '(width >= 87.5rem)',\n} as const;\n","import { DOCUMENT } from '@angular/common';\nimport { DestroyRef, Injectable, InjectionToken, computed, inject, signal } from '@angular/core';\n\nexport type RbTheme = 'light' | 'dark';\n\n/** localStorage key holding the user's explicit choice (docs/design-system.md, section 11). */\nexport const RB_THEME_STORAGE_KEY = 'rb-theme';\n\n/** `--rb-color-bg` per theme; also the `<meta name=\"theme-color\">` value. */\nexport const RB_THEME_COLORS: Readonly<Record<RbTheme, string>> = {\n light: '#f4f7fb',\n dark: '#08111d',\n};\n\nexport interface RbThemeOptions {\n /**\n * Forces a theme regardless of the stored choice and the OS, without persisting anything. Used by\n * apps that only support one theme until their migration is done. The theme toggle hides itself\n * while a theme is forced.\n */\n forcedTheme?: RbTheme | null;\n /** Storage keys an app used before adopting the kit; a valid value is moved to `rb-theme` once. */\n legacyStorageKeys?: readonly string[];\n}\n\nexport const RB_THEME_OPTIONS = new InjectionToken<RbThemeOptions>('RB_THEME_OPTIONS', {\n providedIn: 'root',\n factory: () => ({}),\n});\n\nconst DARK_SCHEME_QUERY = '(prefers-color-scheme: dark)';\n\nfunction isTheme(value: unknown): value is RbTheme {\n return value === 'light' || value === 'dark';\n}\n\n/**\n * Owns the light/dark theme contract (docs/design-system.md, section 11):\n *\n * - an explicit choice is stored under `rb-theme` and wins over the OS;\n * - without one, the theme follows `prefers-color-scheme` and tracks OS changes live;\n * - the resolved theme is written to `<html>` as `data-theme`, and every\n * `<meta name=\"theme-color\">` is kept in sync;\n * - `color-scheme` is only set inline for an explicit or forced theme; otherwise the stylesheet's\n * `color-scheme: light dark` already follows the OS before any script runs.\n *\n * `provideRbDesignSystem()` calls `initialize()` from an app initializer, before first paint.\n */\n@Injectable({ providedIn: 'root' })\nexport class ThemeService {\n private readonly document = inject(DOCUMENT);\n private readonly options = inject(RB_THEME_OPTIONS);\n private readonly window = this.document.defaultView;\n private readonly darkScheme = this.window?.matchMedia?.(DARK_SCHEME_QUERY) ?? null;\n\n private readonly systemTheme = signal<RbTheme>(this.darkScheme?.matches ? 'dark' : 'light');\n private readonly storedTheme = signal<RbTheme | null>(null);\n private readonly forcedTheme = signal<RbTheme | null>(this.options.forcedTheme ?? null);\n private initialized = false;\n\n /** The user's explicit choice, or `null` while the theme follows the OS. */\n readonly preference = this.storedTheme.asReadonly();\n /** The theme in effect: forced, then stored, then the OS preference. */\n readonly theme = computed<RbTheme>(\n () => this.forcedTheme() ?? this.storedTheme() ?? this.systemTheme(),\n );\n readonly isDark = computed(() => this.theme() === 'dark');\n /** True while a theme is forced; the toggle is hidden then. */\n readonly isForced = computed(() => this.forcedTheme() !== null);\n\n constructor() {\n const onSystemChange = (event: MediaQueryListEvent): void => {\n this.systemTheme.set(event.matches ? 'dark' : 'light');\n this.apply();\n };\n this.darkScheme?.addEventListener('change', onSystemChange);\n inject(DestroyRef).onDestroy(() =>\n this.darkScheme?.removeEventListener('change', onSystemChange),\n );\n }\n\n /** Reads the stored choice and applies the theme to the document. Safe to call more than once. */\n initialize(): void {\n if (!this.initialized) {\n this.initialized = true;\n this.storedTheme.set(this.readStoredTheme());\n }\n this.apply();\n }\n\n /** Switches between light and dark and stores the result as an explicit choice. */\n toggle(): void {\n this.setTheme(this.theme() === 'dark' ? 'light' : 'dark');\n }\n\n /** Stores an explicit choice; OS changes are ignored from now on. */\n setTheme(theme: RbTheme): void {\n this.initialized = true;\n this.storedTheme.set(theme);\n this.write(RB_THEME_STORAGE_KEY, theme);\n this.apply();\n }\n\n /** Forgets the explicit choice, so the theme follows the OS again. */\n clearPreference(): void {\n this.initialized = true;\n this.storedTheme.set(null);\n this.remove(RB_THEME_STORAGE_KEY);\n this.apply();\n }\n\n /** Forces a theme (or releases it with `null`) without touching the stored choice. */\n forceTheme(theme: RbTheme | null): void {\n this.forcedTheme.set(theme);\n this.apply();\n }\n\n private apply(): void {\n const root = this.document.documentElement;\n const theme = this.theme();\n const explicit = this.forcedTheme() ?? this.storedTheme();\n\n root.dataset['theme'] = theme;\n if (explicit) {\n root.style.colorScheme = explicit;\n } else {\n root.style.removeProperty('color-scheme');\n }\n\n for (const meta of Array.from(\n this.document.querySelectorAll<HTMLMetaElement>('meta[name=\"theme-color\"]'),\n )) {\n meta.content = RB_THEME_COLORS[theme];\n }\n }\n\n private readStoredTheme(): RbTheme | null {\n const stored = this.read(RB_THEME_STORAGE_KEY);\n let theme: RbTheme | null = isTheme(stored) ? stored : null;\n\n for (const legacyKey of this.options.legacyStorageKeys ?? []) {\n const legacy = this.read(legacyKey);\n if (theme === null && isTheme(legacy)) {\n theme = legacy;\n this.write(RB_THEME_STORAGE_KEY, legacy);\n }\n if (legacy !== null) this.remove(legacyKey);\n }\n\n return theme;\n }\n\n // Storage can be unavailable (privacy modes, disabled storage); the theme then follows the OS.\n private read(key: string): string | null {\n try {\n return this.window?.localStorage.getItem(key) ?? null;\n } catch {\n return null;\n }\n }\n\n private write(key: string, value: string): void {\n try {\n this.window?.localStorage.setItem(key, value);\n } catch {\n // Ignore: the choice still applies for this session.\n }\n }\n\n private remove(key: string): void {\n try {\n this.window?.localStorage.removeItem(key);\n } catch {\n // Ignore: nothing to clean up without storage.\n }\n }\n}\n","import {\n EnvironmentProviders,\n inject,\n makeEnvironmentProviders,\n provideAppInitializer,\n} from '@angular/core';\nimport { MatIconRegistry } from '@angular/material/icon';\n\nimport { RB_THEME_OPTIONS, RbThemeOptions, ThemeService } from './theme.service';\n\n/** Font set class of the self-hosted Material Symbols subset (docs/design-system.md, section 10). */\nexport const RB_ICON_FONT_SET = 'material-symbols-outlined';\n\nexport type RbDesignSystemOptions = RbThemeOptions;\n\n/**\n * Registers the kit's runtime pieces in `app.config.ts`:\n *\n * - applies the theme before the root component renders (no flash for a stored choice);\n * - makes `material-symbols-outlined` the default `<mat-icon>` font set.\n */\nexport function provideRbDesignSystem(options: RbDesignSystemOptions = {}): EnvironmentProviders {\n return makeEnvironmentProviders([\n { provide: RB_THEME_OPTIONS, useValue: options },\n provideAppInitializer(() => {\n inject(ThemeService).initialize();\n inject(MatIconRegistry).setDefaultFontSetClass(RB_ICON_FONT_SET);\n }),\n ]);\n}\n","/** Tones of `rb-message` (docs/design-system.md, section 15.3). */\nexport type RbMessageTone = 'neutral' | 'info' | 'success' | 'warning' | 'danger';\n\n/** Tones of `rb-status-chip` and page header metrics (15.1, 15.5). `active` means in progress. */\nexport type RbStatusTone = 'neutral' | 'active' | 'success' | 'warning' | 'danger';\n\n/**\n * Default icon per tone, so state is never carried by color alone (section 9.4). Neutral status\n * chips and metrics have no default icon: their label is the whole message.\n */\nexport const RB_TONE_ICONS: Readonly<Record<RbMessageTone | RbStatusTone, string | null>> = {\n neutral: null,\n info: 'info',\n active: 'autorenew',\n success: 'check_circle',\n warning: 'warning',\n danger: 'error',\n};\n\n/** Visually hidden prefix that names a message's tone for screen reader users. */\nexport const RB_MESSAGE_TONE_LABELS: Readonly<Record<RbMessageTone, string | null>> = {\n neutral: null,\n info: 'Information',\n success: 'Success',\n warning: 'Warning',\n danger: 'Error',\n};\n","/**\n * Kit version. The build stamps it from projects/axiom/package.json (scripts/stamp-version.mjs);\n * source and development builds show the placeholder.\n */\nexport const RB_DESIGN_SYSTEM_VERSION: string = '1.3.0';\n"],"mappings":"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"}
@@ -522,6 +522,20 @@ const ICON_TILES = [
522
522
  label: "Registry audit"
523
523
  }
524
524
  ];
525
+ const STAGE_METRICS = [
526
+ {
527
+ label: "Bitrate",
528
+ value: "2.4 Mbps"
529
+ },
530
+ {
531
+ label: "FPS",
532
+ value: "30"
533
+ },
534
+ {
535
+ label: "Resolution",
536
+ value: "1280 × 720"
537
+ }
538
+ ];
525
539
  const MONOGRAM_SIZES = [
526
540
  {
527
541
  token: "--rb-size-monogram",
@@ -607,6 +621,7 @@ var DesignSystemShowcaseComponent = class DesignSystemShowcaseComponent {
607
621
  buttonRoles = BUTTON_ROLES;
608
622
  monogramSizes = MONOGRAM_SIZES;
609
623
  iconTiles = ICON_TILES;
624
+ stageMetrics = STAGE_METRICS;
610
625
  example = toSignal(inject(ActivatedRoute).queryParamMap.pipe(map((params) => {
611
626
  const kind = params.get("example");
612
627
  return kind && RB_SHELL_EXAMPLE_KINDS.includes(kind) ? kind : null;
@@ -685,8 +700,8 @@ var DesignSystemShowcaseComponent = class DesignSystemShowcaseComponent {
685
700
  classAttribute: "rb-scope"
686
701
  },
687
702
  ngImport: i0,
688
- template: "@if (example(); as kind) {\n <rb-showcase-shell-example [kind]=\"kind\" />\n} @else {\n <!-- Focuses main itself: under <base href=\"/\"> the bare fragment would load the root route. -->\n <a\n class=\"rb-skip-link\"\n href=\"#rb-showcase-main\"\n (click)=\"$event.preventDefault(); showcaseMain.focus()\"\n >Skip to main content</a\n >\n\n <main\n #showcaseMain\n id=\"rb-showcase-main\"\n class=\"showcase__main\"\n tabindex=\"-1\"\n aria-label=\"Design system reference\"\n >\n <rb-page-header\n class=\"showcase__header\"\n eyebrow=\"RB Labs Design System\"\n title=\"Foundations and components\"\n [description]=\"\n theme.preference() === null\n ? 'Tokens, Material components and the content kit, following the operating system theme.'\n : 'Tokens, Material components and the content kit.'\n \"\n [metrics]=\"headerMetrics()\"\n >\n <rb-theme-toggle rbActions />\n </rb-page-header>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-color\">\n <h2 id=\"showcase-color\" class=\"showcase__heading\">Color roles</h2>\n @for (group of colorGroups; track group.title) {\n <h3 class=\"showcase__subheading\">{{ group.title }}</h3>\n <ul class=\"showcase__swatches\">\n @for (role of group.roles; track role.token) {\n <li class=\"showcase__swatch\">\n <span\n class=\"showcase__chip\"\n [style.background-color]=\"'var(--rb-color-' + role.token + ')'\"\n [style.color]=\"role.on ? 'var(--rb-color-' + role.on + ')' : null\"\n aria-hidden=\"true\"\n >{{ role.on ? 'Aa' : '' }}</span\n >\n <code>--rb-color-{{ role.token }}</code>\n @if (role.on) {\n <span class=\"showcase__note\">text: {{ role.on }}</span>\n }\n </li>\n }\n </ul>\n }\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-type\">\n <h2 id=\"showcase-type\" class=\"showcase__heading\">Type scale</h2>\n <ul class=\"showcase__list\">\n @for (role of typeRoles; track role) {\n <li class=\"showcase__type-row\">\n <code>{{ role }}</code>\n <span\n class=\"showcase__type-sample\"\n [style.font]=\"'var(--rb-type-' + role + ')'\"\n [style.letter-spacing]=\"'var(--rb-type-' + role + '-tracking)'\"\n >Operate RB Labs</span\n >\n </li>\n }\n </ul>\n <p class=\"showcase__mono\">ui-monospace · usr_01HZX9 · sha256:2b4f69c9</p>\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-space\">\n <h2 id=\"showcase-space\" class=\"showcase__heading\">Spacing</h2>\n <ul class=\"showcase__list\">\n @for (step of spaceSteps; track step) {\n <li class=\"showcase__space-row\">\n <code>--rb-space-{{ step }}</code>\n <span\n class=\"showcase__space-bar\"\n [style.inline-size]=\"'var(--rb-space-' + step + ')'\"\n aria-hidden=\"true\"\n ></span>\n </li>\n }\n </ul>\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-shape\">\n <h2 id=\"showcase-shape\" class=\"showcase__heading\">Radius and elevation</h2>\n <ul class=\"showcase__tiles\">\n @for (radius of radii; track radius) {\n <li class=\"showcase__tile\" [style.border-radius]=\"'var(--rb-radius-' + radius + ')'\">\n <code>radius-{{ radius }}</code>\n </li>\n }\n </ul>\n <ul class=\"showcase__tiles\">\n @for (level of elevations; track level) {\n <li\n class=\"showcase__tile showcase__tile--raised\"\n [style.box-shadow]=\"'var(--rb-elevation-' + level + ')'\"\n >\n <code>elevation-{{ level }}</code>\n </li>\n }\n </ul>\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-material\">\n <h2 id=\"showcase-material\" class=\"showcase__heading\">Buttons</h2>\n <p class=\"showcase__lead\">\n There is no kit button component. Each role maps to an Angular Material button variant.\n </p>\n <div class=\"rb-table-scroll\">\n <table class=\"rb-table\">\n <caption class=\"rb-visually-hidden\">\n Button roles and their Material markup\n </caption>\n <thead>\n <tr>\n <th scope=\"col\">Role</th>\n <th scope=\"col\">Markup</th>\n <th scope=\"col\">Equivalent selector</th>\n </tr>\n </thead>\n <tbody>\n @for (button of buttonRoles; track button.role) {\n <tr>\n <td data-label=\"Role\">{{ button.role }}</td>\n <td data-label=\"Markup\">\n <code>{{ button.markup }}</code>\n </td>\n <td data-label=\"Equivalent selector\">\n <code>{{ button.legacy }}</code>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n <div class=\"rb-action-group\">\n <button matButton=\"filled\" type=\"button\">Primary</button>\n <button matButton=\"outlined\" type=\"button\">Secondary</button>\n <button matButton type=\"button\">Tertiary</button>\n <button matButton=\"tonal\" type=\"button\">Tonal</button>\n <button matButton=\"filled\" type=\"button\" disabled>Disabled</button>\n </div>\n <div class=\"rb-action-group\">\n <button matButton=\"filled\" color=\"warn\" type=\"button\">\n <mat-icon aria-hidden=\"true\">delete</mat-icon>\n Delete\n </button>\n <button matButton=\"outlined\" color=\"warn\" type=\"button\">Revoke</button>\n <button matButton color=\"warn\" type=\"button\">Remove</button>\n <button\n matIconButton\n color=\"warn\"\n type=\"button\"\n aria-label=\"Delete item\"\n matTooltip=\"Delete item\"\n >\n <mat-icon aria-hidden=\"true\">delete</mat-icon>\n </button>\n <button matIconButton type=\"button\" aria-label=\"Edit item\" matTooltip=\"Edit item\">\n <mat-icon aria-hidden=\"true\">edit</mat-icon>\n </button>\n </div>\n <p class=\"showcase__lead\">\n A link that navigates uses the same variants on an <code>&lt;a&gt;</code>. On coarse\n pointers it reaches the touch target like a button.\n </p>\n <div class=\"rb-action-group\">\n <a matButton=\"filled\" routerLink=\".\" fragment=\"showcase-material\">Primary link</a>\n <a matButton=\"outlined\" routerLink=\".\" fragment=\"showcase-material\">Secondary link</a>\n <a matButton routerLink=\".\" fragment=\"showcase-material\">Tertiary link</a>\n <a matButton=\"tonal\" routerLink=\".\" fragment=\"showcase-material\">Tonal link</a>\n <a\n matIconButton\n routerLink=\".\"\n fragment=\"showcase-material\"\n aria-label=\"Go to buttons\"\n matTooltip=\"Go to buttons\"\n >\n <mat-icon aria-hidden=\"true\">arrow_forward</mat-icon>\n </a>\n </div>\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-shells\">\n <h2 id=\"showcase-shells\" class=\"showcase__heading\">Shells and brand</h2>\n\n <h3 class=\"showcase__subheading\">Brand lockup and monogram</h3>\n <div class=\"showcase__brand-row\">\n <rb-brand-lockup appName=\"Admin\" link=\"/design-system\" />\n <rb-brand-lockup appName=\"Photogrammetry\" link=\"/design-system\" />\n <rb-brand-lockup appName=\"Rover\" link=\"/design-system\" compact />\n </div>\n <ul class=\"showcase__brand-row showcase__brand-row--list\" aria-label=\"Monogram sizes\">\n @for (size of monogramSizes; track size.token) {\n <li class=\"showcase__monogram\">\n <rb-monogram [style.inline-size]=\"size.value\" />\n <code>{{ size.token }}</code>\n </li>\n }\n </ul>\n\n <p class=\"showcase__lead\">\n Each preview is a frame whose width is the viewport, so on narrow screens the sidenav\n becomes a drawer and the module nav a tab bar. The previews are inert, so their landmarks do\n not repeat in this page; open the full page to use an example.\n </p>\n @for (example of shellExamples; track example.kind + theme.theme()) {\n <h3 class=\"showcase__subheading\">{{ example.title }}</h3>\n <p class=\"showcase__lead\">\n {{ example.description }}\n <a routerLink=\".\" [queryParams]=\"{ example: example.kind }\">Open full page</a>\n </p>\n <iframe\n class=\"showcase__frame\"\n [title]=\"example.title + ' example'\"\n [src]=\"example.src\"\n loading=\"lazy\"\n inert\n ></iframe>\n }\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-components\">\n <h2 id=\"showcase-components\" class=\"showcase__heading\">Content components</h2>\n\n <h3 class=\"showcase__subheading\">Page header</h3>\n <p class=\"showcase__lead\">\n The header of this page is an <code>rb-page-header</code> with an eyebrow, description,\n metrics and the theme toggle as its action. A page has exactly one.\n </p>\n\n <h3 class=\"showcase__subheading\">Panel</h3>\n <div class=\"showcase__grid\">\n <rb-panel title=\"Default\" description=\"Cards and grouped content.\" [headingLevel]=\"4\">\n <button rbActions matButton type=\"button\">\n <mat-icon aria-hidden=\"true\">refresh</mat-icon>\n Refresh\n </button>\n <p class=\"showcase__text\">Surface background, border and elevation 1.</p>\n </rb-panel>\n <rb-panel\n title=\"Soft\"\n description=\"Secondary or inset content.\"\n variant=\"soft\"\n [headingLevel]=\"4\"\n >\n <p class=\"showcase__text\">Sunken surface, no shadow.</p>\n </rb-panel>\n <rb-panel\n title=\"Elevated\"\n description=\"Content that floats above the page.\"\n variant=\"elevated\"\n [headingLevel]=\"4\"\n >\n <p class=\"showcase__text\">Raised surface and elevation 2.</p>\n </rb-panel>\n <rb-panel\n title=\"Danger zone\"\n description=\"Destructive settings.\"\n variant=\"danger\"\n [headingLevel]=\"4\"\n >\n <button rbActions matButton=\"outlined\" color=\"warn\" type=\"button\">Delete</button>\n <p class=\"showcase__text\">Danger border and title; the title names the risk.</p>\n </rb-panel>\n </div>\n\n <h3 class=\"showcase__subheading\">Message</h3>\n <div class=\"rb-stack rb-stack--sm\">\n @for (sample of messageSamples; track sample.tone) {\n <rb-message [tone]=\"sample.tone\" [title]=\"sample.title\" live=\"off\">\n {{ sample.body }}\n </rb-message>\n }\n </div>\n\n <h3 class=\"showcase__subheading\">Empty state</h3>\n <div class=\"showcase__grid\">\n <rb-panel title=\"Applications\" [headingLevel]=\"4\">\n <rb-empty-state\n icon=\"apps\"\n title=\"No applications yet\"\n description=\"Register a client to issue credentials for RB Labs sign-in.\"\n actionLabel=\"Register application\"\n [headingLevel]=\"5\"\n />\n </rb-panel>\n <rb-panel title=\"Audit history\" variant=\"soft\" [headingLevel]=\"4\">\n <rb-empty-state\n icon=\"history\"\n title=\"Nothing recorded\"\n description=\"Changes to this application appear here.\"\n layout=\"inline\"\n [headingLevel]=\"5\"\n />\n </rb-panel>\n </div>\n\n <h3 class=\"showcase__subheading\">Status chip</h3>\n <div class=\"rb-action-group\">\n @for (chip of chipSamples; track chip.tone) {\n <rb-status-chip [tone]=\"chip.tone\" [label]=\"chip.label\" />\n }\n </div>\n <div class=\"rb-action-group\">\n @for (chip of chipSamples; track chip.tone) {\n <rb-status-chip [tone]=\"chip.tone\" [label]=\"chip.label\" size=\"compact\" />\n }\n <rb-status-chip label=\"Locked\" icon=\"lock\" size=\"compact\" />\n </div>\n\n <h3 class=\"showcase__subheading\">Technical value</h3>\n <dl class=\"rb-meta-grid\">\n <div class=\"rb-meta-grid__item\">\n <dt class=\"rb-meta-grid__label\">Fits</dt>\n <dd class=\"rb-meta-grid__value\"><rb-technical-value value=\"usr_01HZX9\" /></dd>\n </div>\n <div class=\"rb-meta-grid__item showcase__narrow\">\n <dt class=\"rb-meta-grid__label\">Truncated</dt>\n <dd class=\"rb-meta-grid__value\">\n <rb-technical-value\n value=\"https://auth.rblabs.dev/oauth/callback/photogrammetry-workspace\"\n />\n </dd>\n </div>\n <div class=\"rb-meta-grid__item\">\n <dt class=\"rb-meta-grid__label\">Shortened</dt>\n <dd class=\"rb-meta-grid__value\">\n <rb-technical-value\n value=\"sha256:2b4f69c9d1e0a7b35c8e6f41a9d2c7b0e3f5a8c1d4b7e0f2a5c8d1e4b7a0c3f6\"\n displayValue=\"sha256:2b4f69c9…\"\n />\n </dd>\n </div>\n </dl>\n\n <h3 class=\"showcase__subheading\">Confirmation dialog</h3>\n <div class=\"rb-action-group\">\n <button matButton=\"outlined\" type=\"button\" (click)=\"openConfirm('primary')\">\n Confirm an action\n </button>\n <button matButton=\"outlined\" color=\"warn\" type=\"button\" (click)=\"openConfirm('danger')\">\n Confirm a destructive action\n </button>\n <button matButton=\"outlined\" color=\"warn\" type=\"button\" (click)=\"openConfirm('reason')\">\n Confirm with a reason\n </button>\n </div>\n <rb-message [tone]=\"dialogResultTone()\" title=\"Last result\">{{ dialogResult() }}</rb-message>\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-classes\">\n <h2 id=\"showcase-classes\" class=\"showcase__heading\">Style classes</h2>\n\n <h3 class=\"showcase__subheading\">Forms</h3>\n <form class=\"rb-form-grid\" (submit)=\"$event.preventDefault()\">\n <div class=\"rb-field\">\n <label class=\"rb-field__label\" for=\"showcase-field-name\">Application name</label>\n <input\n id=\"showcase-field-name\"\n class=\"rb-field__control\"\n type=\"text\"\n value=\"Photogrammetry workspace\"\n aria-describedby=\"showcase-field-name-hint\"\n />\n <p id=\"showcase-field-name-hint\" class=\"rb-field__hint\">Shown on the consent screen.</p>\n </div>\n <div class=\"rb-field\">\n <label class=\"rb-field__label\" for=\"showcase-field-environment\">Environment</label>\n <select id=\"showcase-field-environment\" class=\"rb-field__control\">\n <option>Production</option>\n <option>Staging</option>\n </select>\n </div>\n <div class=\"rb-field rb-field--invalid\">\n <label class=\"rb-field__label\" for=\"showcase-field-uri\">Redirect URI</label>\n <input\n id=\"showcase-field-uri\"\n class=\"rb-field__control\"\n type=\"url\"\n value=\"http://photogrammetry\"\n aria-invalid=\"true\"\n aria-describedby=\"showcase-field-uri-error\"\n />\n <p id=\"showcase-field-uri-error\" class=\"rb-field__error\">\n Enter an https:// address, for example https://app.rblabs.dev/callback.\n </p>\n </div>\n <div class=\"rb-field rb-field--disabled\">\n <label class=\"rb-field__label\" for=\"showcase-field-client\">Client ID</label>\n <input\n id=\"showcase-field-client\"\n class=\"rb-field__control\"\n type=\"text\"\n value=\"app_01J2B7F9P4L6X3V8S1D5G0HAKM\"\n disabled\n aria-describedby=\"showcase-field-client-hint\"\n />\n <p id=\"showcase-field-client-hint\" class=\"rb-field__hint\">\n Generated at registration and cannot be changed.\n </p>\n </div>\n <div class=\"rb-field rb-form-grid__full\">\n <label class=\"rb-field__label\" for=\"showcase-field-notes\">Notes</label>\n <textarea\n id=\"showcase-field-notes\"\n class=\"rb-field__control\"\n placeholder=\"Optional context for other administrators\"\n ></textarea>\n </div>\n <mat-form-field appearance=\"outline\">\n <mat-label>Display name (mat-form-field)</mat-label>\n <input matInput value=\"Photogrammetry\" />\n <mat-hint>Material fields sit in the same grid.</mat-hint>\n </mat-form-field>\n <div class=\"rb-field rb-form-grid__full\">\n <span id=\"showcase-field-scopes\" class=\"rb-field__label\"\n >Granted scopes (mat-chip-grid)</span\n >\n @if (scopes().length > 0) {\n <mat-chip-grid aria-labelledby=\"showcase-field-scopes\">\n @for (scope of scopes(); track scope) {\n <mat-chip-row (removed)=\"removeScope(scope)\">\n {{ scope }}\n <button matChipRemove [attr.aria-label]=\"'Remove ' + scope\">\n <mat-icon aria-hidden=\"true\">close</mat-icon>\n </button>\n </mat-chip-row>\n }\n </mat-chip-grid>\n } @else {\n <p class=\"rb-field__hint\">No scopes granted.</p>\n }\n <p class=\"rb-field__hint\">\n Remove buttons are at least 24px on fine pointers and 44px on coarse pointers.\n @if (scopes().length < defaultScopes.length) {\n <button matButton type=\"button\" (click)=\"resetScopes()\">Restore scopes</button>\n }\n </p>\n </div>\n </form>\n\n <h3 class=\"showcase__subheading\">Tables</h3>\n <p class=\"showcase__lead\">\n Below the <code>md</code> breakpoint each row becomes a stacked record labelled from\n <code>data-label</code>.\n </p>\n <div class=\"rb-table-scroll\">\n <table class=\"rb-table\">\n <caption class=\"rb-visually-hidden\">\n Registered applications\n </caption>\n <thead>\n <tr>\n <th scope=\"col\">Name</th>\n <th scope=\"col\">Client ID</th>\n <th scope=\"col\">Status</th>\n <th scope=\"col\">Updated</th>\n <th scope=\"col\"><span class=\"rb-visually-hidden\">Actions</span></th>\n </tr>\n </thead>\n <tbody>\n @for (row of tableRows; track row.clientId) {\n <tr>\n <td data-label=\"Name\">{{ row.name }}</td>\n <td data-label=\"Client ID\" class=\"showcase__cell-id\">\n <rb-technical-value [value]=\"row.clientId\" />\n </td>\n <td data-label=\"Status\">\n <rb-status-chip\n [tone]=\"row.status.tone\"\n [label]=\"row.status.label\"\n size=\"compact\"\n />\n </td>\n <td data-label=\"Updated\">{{ row.updated }}</td>\n <td>\n <div class=\"rb-table__actions\">\n <button\n matIconButton\n type=\"button\"\n [attr.aria-label]=\"'Edit ' + row.name\"\n [matTooltip]=\"'Edit ' + row.name\"\n >\n <mat-icon aria-hidden=\"true\">edit</mat-icon>\n </button>\n <button\n matIconButton\n color=\"warn\"\n type=\"button\"\n [attr.aria-label]=\"'Delete ' + row.name\"\n [matTooltip]=\"'Delete ' + row.name\"\n >\n <mat-icon aria-hidden=\"true\">delete</mat-icon>\n </button>\n </div>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <div class=\"showcase__grid\">\n <div class=\"rb-table-scroll\">\n <table class=\"rb-table\" aria-busy=\"true\">\n <caption class=\"rb-visually-hidden\">\n Applications, loading\n </caption>\n <thead>\n <tr>\n <th scope=\"col\">Name</th>\n <th scope=\"col\">Status</th>\n </tr>\n </thead>\n <tbody>\n @for (row of [1, 2, 3]; track row) {\n <tr class=\"rb-table__skeleton-row\" aria-hidden=\"true\">\n <td><span class=\"rb-skeleton\"></span></td>\n <td><span class=\"rb-skeleton\"></span></td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n <div class=\"rb-table-scroll\">\n <table class=\"rb-table\">\n <caption class=\"rb-visually-hidden\">\n Applications matching “rover”\n </caption>\n <thead>\n <tr>\n <th scope=\"col\">Name</th>\n <th scope=\"col\">Status</th>\n </tr>\n </thead>\n <tbody>\n <tr class=\"rb-table__empty-row\">\n <td colspan=\"2\">No applications match “rover”.</td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n\n <p class=\"showcase__lead\">\n <code>mat-table</code> with <code>rb-table--inline</code>: the grid is kept on phones and\n scrolls inside <code>rb-table-scroll</code>, which is focusable so keyboard users can scroll\n it.\n </p>\n <div\n class=\"rb-table-scroll\"\n role=\"region\"\n aria-label=\"Registered applications, scrollable\"\n tabindex=\"0\"\n >\n <table mat-table class=\"rb-table rb-table--inline\" [dataSource]=\"tableRows\">\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef scope=\"col\">Name</th>\n <td mat-cell *matCellDef=\"let row\" data-label=\"Name\">{{ row.name }}</td>\n </ng-container>\n <ng-container matColumnDef=\"clientId\">\n <th mat-header-cell *matHeaderCellDef scope=\"col\">Client ID</th>\n <td mat-cell *matCellDef=\"let row\" data-label=\"Client ID\">\n <rb-technical-value [value]=\"row.clientId\" />\n </td>\n </ng-container>\n <ng-container matColumnDef=\"status\">\n <th mat-header-cell *matHeaderCellDef scope=\"col\">Status</th>\n <td mat-cell *matCellDef=\"let row\" data-label=\"Status\">\n <rb-status-chip [tone]=\"row.status.tone\" [label]=\"row.status.label\" size=\"compact\" />\n </td>\n </ng-container>\n <ng-container matColumnDef=\"updated\">\n <th mat-header-cell *matHeaderCellDef scope=\"col\">Updated</th>\n <td mat-cell *matCellDef=\"let row\" data-label=\"Updated\">{{ row.updated }}</td>\n </ng-container>\n <tr mat-header-row *matHeaderRowDef=\"tableColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: tableColumns\"></tr>\n <tr *matNoDataRow>\n <td [attr.colspan]=\"tableColumns.length\">No applications.</td>\n </tr>\n </table>\n </div>\n\n <h3 class=\"showcase__subheading\">Layout</h3>\n <div class=\"rb-master-detail\">\n <rb-panel\n title=\"rb-master-detail\"\n description=\"List on the left, detail on the right.\"\n variant=\"soft\"\n [headingLevel]=\"4\"\n >\n <div class=\"rb-stack rb-stack--sm\">\n <span class=\"rb-skeleton\" aria-hidden=\"true\"></span>\n <span class=\"rb-skeleton\" aria-hidden=\"true\"></span>\n <span class=\"rb-skeleton\" aria-hidden=\"true\"></span>\n </div>\n </rb-panel>\n <rb-panel title=\"Detail\" description=\"Stacks below the xl breakpoint.\" [headingLevel]=\"4\">\n <div class=\"rb-stack\">\n <dl class=\"rb-meta-grid\">\n <div class=\"rb-meta-grid__item\">\n <dt class=\"rb-meta-grid__label\">Owner</dt>\n <dd class=\"rb-meta-grid__value\">Platform team</dd>\n </div>\n <div class=\"rb-meta-grid__item\">\n <dt class=\"rb-meta-grid__label\">Created</dt>\n <dd class=\"rb-meta-grid__value\">3 Mar 2026</dd>\n </div>\n <div class=\"rb-meta-grid__item\">\n <dt class=\"rb-meta-grid__label\">Scopes</dt>\n <dd class=\"rb-meta-grid__value\">openid profile</dd>\n </div>\n </dl>\n <span class=\"rb-skeleton rb-skeleton--heading\" aria-hidden=\"true\"></span>\n <span class=\"rb-skeleton rb-skeleton--control\" aria-hidden=\"true\"></span>\n <div class=\"rb-action-group rb-action-group--between rb-action-group--mobile-stack\">\n <button matButton color=\"warn\" type=\"button\">Delete</button>\n <div class=\"rb-action-group rb-action-group--end rb-action-group--mobile-stack\">\n <button matButton=\"outlined\" type=\"button\">Cancel</button>\n <button matButton=\"filled\" type=\"button\">Save changes</button>\n </div>\n </div>\n </div>\n </rb-panel>\n </div>\n\n <h3 class=\"showcase__subheading\">Text and landing</h3>\n <p class=\"showcase__lead\">\n The hero, tags and icon tiles of the public home pages, and the two uppercase label roles.\n </p>\n <div class=\"rb-hero\">\n <div class=\"rb-hero__copy\">\n <p class=\"rb-eyebrow\">\n <mat-icon aria-hidden=\"true\">deployed_code</mat-icon>\n rb-eyebrow\n </p>\n <p class=\"rb-hero__title\">One standard for every RB Labs app</p>\n <p class=\"rb-hero__lead\">\n <code>.rb-hero__title</code> uses the hero type role; <code>.rb-hero__lead</code> sits\n under it. Below the lg breakpoint the media column moves under the copy.\n </p>\n <ul class=\"rb-tag-list\" aria-label=\"Stack\">\n <li>Angular</li>\n <li>Angular Material 3</li>\n <li>WCAG 2.2 AA</li>\n </ul>\n </div>\n <rb-panel title=\"Media column\" variant=\"soft\" [headingLevel]=\"4\">\n <ul class=\"showcase__icon-tiles\">\n @for (tile of iconTiles; track tile.label) {\n <li>\n <span class=\"rb-icon-tile\" aria-hidden=\"true\">\n <mat-icon>{{ tile.icon }}</mat-icon>\n </span>\n <span>\n <span class=\"rb-overline\">{{ tile.overline }}</span>\n <a class=\"rb-table__link\" routerLink=\".\" fragment=\"showcase-classes\">{{\n tile.label\n }}</a>\n </span>\n </li>\n }\n </ul>\n </rb-panel>\n </div>\n </section>\n </main>\n}\n",
689
- styles: [":host{display:block;min-height:100dvh;padding:var(--rb-space-page)}:host(.showcase--example){padding:0}.showcase__main{width:min(100%,var(--rb-size-content-max));margin-inline:auto}.showcase__header{padding-block-end:var(--rb-space-6);border-block-end:1px solid var(--rb-color-border)}.showcase__lead,.showcase__text{margin:0;color:var(--rb-color-text-muted);font:var(--rb-type-body-medium)}.showcase__lead{max-width:48rem}.showcase__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,18rem),1fr));gap:var(--rb-space-4);align-items:start}.showcase__narrow{max-inline-size:12rem}.showcase__cell-id{max-inline-size:14rem}.showcase__note{margin:0;color:var(--rb-color-text-muted);font:var(--rb-type-body-small)}.showcase__main{display:grid;gap:var(--rb-space-10);padding-block-end:var(--rb-space-8)}.showcase__main:focus{outline:none}.showcase__section{display:grid;gap:var(--rb-space-4);min-width:0}.showcase__heading{margin:0;color:var(--rb-color-text-strong);font:var(--rb-type-title-large)}.showcase__subheading{margin:var(--rb-space-2) 0 0;color:var(--rb-color-text-muted);font:var(--rb-type-title-small)}.showcase__swatches,.showcase__tiles,.showcase__list{margin:0;padding:0;list-style:none}.showcase__swatches{display:grid;grid-template-columns:repeat(auto-fill,minmax(12rem,1fr));gap:var(--rb-space-3)}.showcase__swatch{display:grid;gap:var(--rb-space-1);min-width:0}.showcase__swatch code{overflow-wrap:anywhere;color:var(--rb-color-text);font-size:var(--rb-type-body-small-size)}.showcase__chip{display:grid;height:3.5rem;place-items:center;border:1px solid var(--rb-color-border);border-radius:var(--rb-radius-md);font:var(--rb-type-title-medium)}.showcase__list{display:grid;gap:var(--rb-space-3)}.showcase__type-row,.showcase__space-row{display:grid;grid-template-columns:9rem minmax(0,1fr);gap:var(--rb-space-4);align-items:center}.showcase__type-row code,.showcase__space-row code{color:var(--rb-color-text-subtle);font-size:var(--rb-type-body-small-size)}@media(width<40rem){.showcase__type-row,.showcase__space-row{grid-template-columns:1fr;gap:var(--rb-space-1)}}.showcase__type-sample{overflow-wrap:anywhere;color:var(--rb-color-text-strong)}.showcase__mono{margin:0;color:var(--rb-color-text);font-family:var(--rb-font-mono)}.showcase__space-bar{display:block;height:var(--rb-space-3);border-radius:var(--rb-radius-xs);background-color:var(--rb-color-primary)}.showcase__tiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(8rem,1fr));gap:var(--rb-space-4)}.showcase__tile{display:grid;min-height:5rem;place-items:center;border:1px solid var(--rb-color-border-strong);border-radius:var(--rb-radius-lg);background-color:var(--rb-color-surface);color:var(--rb-color-text-muted);font-size:var(--rb-type-body-small-size)}.showcase__tile--raised{border-color:transparent;background-color:var(--rb-color-surface-raised)}.showcase__brand-row{display:flex;flex-wrap:wrap;gap:var(--rb-space-6);align-items:center}.showcase__brand-row--list{margin:0;padding:0;list-style:none}.showcase__monogram{display:inline-flex;gap:var(--rb-space-2);align-items:center}.showcase__monogram code{color:var(--rb-color-text-subtle);font-size:var(--rb-type-body-small-size)}.showcase__frame{display:block;inline-size:100%;block-size:40rem;border:1px solid var(--rb-color-border);border-radius:var(--rb-radius-lg);background-color:var(--rb-color-bg)}.showcase__icon-tiles{display:grid;gap:var(--rb-space-4);margin:0;padding:0;list-style:none}.showcase__icon-tiles>li{display:flex;gap:var(--rb-space-3);align-items:center}.showcase__icon-tiles>li>span:last-child{display:grid;gap:var(--rb-space-1)}\n"],
703
+ template: "@if (example(); as kind) {\n <rb-showcase-shell-example [kind]=\"kind\" />\n} @else {\n <!-- Focuses main itself: under <base href=\"/\"> the bare fragment would load the root route. -->\n <a\n class=\"rb-skip-link\"\n href=\"#rb-showcase-main\"\n (click)=\"$event.preventDefault(); showcaseMain.focus()\"\n >Skip to main content</a\n >\n\n <main\n #showcaseMain\n id=\"rb-showcase-main\"\n class=\"showcase__main\"\n tabindex=\"-1\"\n aria-label=\"Design system reference\"\n >\n <rb-page-header\n class=\"showcase__header\"\n eyebrow=\"RB Labs Design System\"\n title=\"Foundations and components\"\n [description]=\"\n theme.preference() === null\n ? 'Tokens, Material components and the content kit, following the operating system theme.'\n : 'Tokens, Material components and the content kit.'\n \"\n [metrics]=\"headerMetrics()\"\n >\n <rb-theme-toggle rbActions />\n </rb-page-header>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-color\">\n <h2 id=\"showcase-color\" class=\"showcase__heading\">Color roles</h2>\n @for (group of colorGroups; track group.title) {\n <h3 class=\"showcase__subheading\">{{ group.title }}</h3>\n <ul class=\"showcase__swatches\">\n @for (role of group.roles; track role.token) {\n <li class=\"showcase__swatch\">\n <span\n class=\"showcase__chip\"\n [style.background-color]=\"'var(--rb-color-' + role.token + ')'\"\n [style.color]=\"role.on ? 'var(--rb-color-' + role.on + ')' : null\"\n aria-hidden=\"true\"\n >{{ role.on ? 'Aa' : '' }}</span\n >\n <code>--rb-color-{{ role.token }}</code>\n @if (role.on) {\n <span class=\"showcase__note\">text: {{ role.on }}</span>\n }\n </li>\n }\n </ul>\n }\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-type\">\n <h2 id=\"showcase-type\" class=\"showcase__heading\">Type scale</h2>\n <ul class=\"showcase__list\">\n @for (role of typeRoles; track role) {\n <li class=\"showcase__type-row\">\n <code>{{ role }}</code>\n <span\n class=\"showcase__type-sample\"\n [style.font]=\"'var(--rb-type-' + role + ')'\"\n [style.letter-spacing]=\"'var(--rb-type-' + role + '-tracking)'\"\n >Operate RB Labs</span\n >\n </li>\n }\n </ul>\n <p class=\"showcase__mono\">ui-monospace · usr_01HZX9 · sha256:2b4f69c9</p>\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-space\">\n <h2 id=\"showcase-space\" class=\"showcase__heading\">Spacing</h2>\n <ul class=\"showcase__list\">\n @for (step of spaceSteps; track step) {\n <li class=\"showcase__space-row\">\n <code>--rb-space-{{ step }}</code>\n <span\n class=\"showcase__space-bar\"\n [style.inline-size]=\"'var(--rb-space-' + step + ')'\"\n aria-hidden=\"true\"\n ></span>\n </li>\n }\n </ul>\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-shape\">\n <h2 id=\"showcase-shape\" class=\"showcase__heading\">Radius and elevation</h2>\n <ul class=\"showcase__tiles\">\n @for (radius of radii; track radius) {\n <li class=\"showcase__tile\" [style.border-radius]=\"'var(--rb-radius-' + radius + ')'\">\n <code>radius-{{ radius }}</code>\n </li>\n }\n </ul>\n <ul class=\"showcase__tiles\">\n @for (level of elevations; track level) {\n <li\n class=\"showcase__tile showcase__tile--raised\"\n [style.box-shadow]=\"'var(--rb-elevation-' + level + ')'\"\n >\n <code>elevation-{{ level }}</code>\n </li>\n }\n </ul>\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-material\">\n <h2 id=\"showcase-material\" class=\"showcase__heading\">Buttons</h2>\n <p class=\"showcase__lead\">\n There is no kit button component. Each role maps to an Angular Material button variant.\n </p>\n <div class=\"rb-table-scroll\">\n <table class=\"rb-table\">\n <caption class=\"rb-visually-hidden\">\n Button roles and their Material markup\n </caption>\n <thead>\n <tr>\n <th scope=\"col\">Role</th>\n <th scope=\"col\">Markup</th>\n <th scope=\"col\">Equivalent selector</th>\n </tr>\n </thead>\n <tbody>\n @for (button of buttonRoles; track button.role) {\n <tr>\n <td data-label=\"Role\">{{ button.role }}</td>\n <td data-label=\"Markup\">\n <code>{{ button.markup }}</code>\n </td>\n <td data-label=\"Equivalent selector\">\n <code>{{ button.legacy }}</code>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n <div class=\"rb-action-group\">\n <button matButton=\"filled\" type=\"button\">Primary</button>\n <button matButton=\"outlined\" type=\"button\">Secondary</button>\n <button matButton type=\"button\">Tertiary</button>\n <button matButton=\"tonal\" type=\"button\">Tonal</button>\n <button matButton=\"filled\" type=\"button\" disabled>Disabled</button>\n </div>\n <div class=\"rb-action-group\">\n <button matButton=\"filled\" color=\"warn\" type=\"button\">\n <mat-icon aria-hidden=\"true\">delete</mat-icon>\n Delete\n </button>\n <button matButton=\"outlined\" color=\"warn\" type=\"button\">Revoke</button>\n <button matButton color=\"warn\" type=\"button\">Remove</button>\n <button\n matIconButton\n color=\"warn\"\n type=\"button\"\n aria-label=\"Delete item\"\n matTooltip=\"Delete item\"\n >\n <mat-icon aria-hidden=\"true\">delete</mat-icon>\n </button>\n <button matIconButton type=\"button\" aria-label=\"Edit item\" matTooltip=\"Edit item\">\n <mat-icon aria-hidden=\"true\">edit</mat-icon>\n </button>\n </div>\n <p class=\"showcase__lead\">\n A link that navigates uses the same variants on an <code>&lt;a&gt;</code>. On coarse\n pointers it reaches the touch target like a button.\n </p>\n <div class=\"rb-action-group\">\n <a matButton=\"filled\" routerLink=\".\" fragment=\"showcase-material\">Primary link</a>\n <a matButton=\"outlined\" routerLink=\".\" fragment=\"showcase-material\">Secondary link</a>\n <a matButton routerLink=\".\" fragment=\"showcase-material\">Tertiary link</a>\n <a matButton=\"tonal\" routerLink=\".\" fragment=\"showcase-material\">Tonal link</a>\n <a\n matIconButton\n routerLink=\".\"\n fragment=\"showcase-material\"\n aria-label=\"Go to buttons\"\n matTooltip=\"Go to buttons\"\n >\n <mat-icon aria-hidden=\"true\">arrow_forward</mat-icon>\n </a>\n </div>\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-shells\">\n <h2 id=\"showcase-shells\" class=\"showcase__heading\">Shells and brand</h2>\n\n <h3 class=\"showcase__subheading\">Brand lockup and monogram</h3>\n <div class=\"showcase__brand-row\">\n <rb-brand-lockup appName=\"Admin\" link=\"/design-system\" />\n <rb-brand-lockup appName=\"Photogrammetry\" link=\"/design-system\" />\n <rb-brand-lockup appName=\"Rover\" link=\"/design-system\" compact />\n </div>\n <ul class=\"showcase__brand-row showcase__brand-row--list\" aria-label=\"Monogram sizes\">\n @for (size of monogramSizes; track size.token) {\n <li class=\"showcase__monogram\">\n <rb-monogram [style.inline-size]=\"size.value\" />\n <code>{{ size.token }}</code>\n </li>\n }\n </ul>\n\n <p class=\"showcase__lead\">\n Each preview is a frame whose width is the viewport, so on narrow screens the sidenav\n becomes a drawer and the module nav a tab bar. The previews are inert, so their landmarks do\n not repeat in this page; open the full page to use an example.\n </p>\n @for (example of shellExamples; track example.kind + theme.theme()) {\n <h3 class=\"showcase__subheading\">{{ example.title }}</h3>\n <p class=\"showcase__lead\">\n {{ example.description }}\n <a routerLink=\".\" [queryParams]=\"{ example: example.kind }\">Open full page</a>\n </p>\n <iframe\n class=\"showcase__frame\"\n [title]=\"example.title + ' example'\"\n [src]=\"example.src\"\n loading=\"lazy\"\n inert\n ></iframe>\n }\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-components\">\n <h2 id=\"showcase-components\" class=\"showcase__heading\">Content components</h2>\n\n <h3 class=\"showcase__subheading\">Page header</h3>\n <p class=\"showcase__lead\">\n The header of this page is an <code>rb-page-header</code> with an eyebrow, description,\n metrics and the theme toggle as its action. A page has exactly one.\n </p>\n\n <h3 class=\"showcase__subheading\">Panel</h3>\n <div class=\"showcase__grid\">\n <rb-panel title=\"Default\" description=\"Cards and grouped content.\" [headingLevel]=\"4\">\n <button rbActions matButton type=\"button\">\n <mat-icon aria-hidden=\"true\">refresh</mat-icon>\n Refresh\n </button>\n <p class=\"showcase__text\">Surface background, border and elevation 1.</p>\n </rb-panel>\n <rb-panel\n title=\"Soft\"\n description=\"Secondary or inset content.\"\n variant=\"soft\"\n [headingLevel]=\"4\"\n >\n <p class=\"showcase__text\">Sunken surface, no shadow.</p>\n </rb-panel>\n <rb-panel\n title=\"Elevated\"\n description=\"Content that floats above the page.\"\n variant=\"elevated\"\n [headingLevel]=\"4\"\n >\n <p class=\"showcase__text\">Raised surface and elevation 2.</p>\n </rb-panel>\n <rb-panel\n title=\"Danger zone\"\n description=\"Destructive settings.\"\n variant=\"danger\"\n [headingLevel]=\"4\"\n >\n <button rbActions matButton=\"outlined\" color=\"warn\" type=\"button\">Delete</button>\n <p class=\"showcase__text\">Danger border and title; the title names the risk.</p>\n </rb-panel>\n </div>\n\n <h3 class=\"showcase__subheading\">Message</h3>\n <div class=\"rb-stack rb-stack--sm\">\n @for (sample of messageSamples; track sample.tone) {\n <rb-message [tone]=\"sample.tone\" [title]=\"sample.title\" live=\"off\">\n {{ sample.body }}\n </rb-message>\n }\n </div>\n\n <h3 class=\"showcase__subheading\">Empty state</h3>\n <div class=\"showcase__grid\">\n <rb-panel title=\"Applications\" [headingLevel]=\"4\">\n <rb-empty-state\n icon=\"apps\"\n title=\"No applications yet\"\n description=\"Register a client to issue credentials for RB Labs sign-in.\"\n actionLabel=\"Register application\"\n [headingLevel]=\"5\"\n />\n </rb-panel>\n <rb-panel title=\"Audit history\" variant=\"soft\" [headingLevel]=\"4\">\n <rb-empty-state\n icon=\"history\"\n title=\"Nothing recorded\"\n description=\"Changes to this application appear here.\"\n layout=\"inline\"\n [headingLevel]=\"5\"\n />\n </rb-panel>\n </div>\n\n <h3 class=\"showcase__subheading\">Status chip</h3>\n <div class=\"rb-action-group\">\n @for (chip of chipSamples; track chip.tone) {\n <rb-status-chip [tone]=\"chip.tone\" [label]=\"chip.label\" />\n }\n </div>\n <div class=\"rb-action-group\">\n @for (chip of chipSamples; track chip.tone) {\n <rb-status-chip [tone]=\"chip.tone\" [label]=\"chip.label\" size=\"compact\" />\n }\n <rb-status-chip label=\"Locked\" icon=\"lock\" size=\"compact\" />\n </div>\n\n <h3 class=\"showcase__subheading\">Technical value</h3>\n <dl class=\"rb-meta-grid\">\n <div class=\"rb-meta-grid__item\">\n <dt class=\"rb-meta-grid__label\">Fits</dt>\n <dd class=\"rb-meta-grid__value\"><rb-technical-value value=\"usr_01HZX9\" /></dd>\n </div>\n <div class=\"rb-meta-grid__item showcase__narrow\">\n <dt class=\"rb-meta-grid__label\">Truncated</dt>\n <dd class=\"rb-meta-grid__value\">\n <rb-technical-value\n value=\"https://auth.rblabs.dev/oauth/callback/photogrammetry-workspace\"\n />\n </dd>\n </div>\n <div class=\"rb-meta-grid__item\">\n <dt class=\"rb-meta-grid__label\">Shortened</dt>\n <dd class=\"rb-meta-grid__value\">\n <rb-technical-value\n value=\"sha256:2b4f69c9d1e0a7b35c8e6f41a9d2c7b0e3f5a8c1d4b7e0f2a5c8d1e4b7a0c3f6\"\n displayValue=\"sha256:2b4f69c9…\"\n />\n </dd>\n </div>\n </dl>\n\n <h3 class=\"showcase__subheading\">Confirmation dialog</h3>\n <div class=\"rb-action-group\">\n <button matButton=\"outlined\" type=\"button\" (click)=\"openConfirm('primary')\">\n Confirm an action\n </button>\n <button matButton=\"outlined\" color=\"warn\" type=\"button\" (click)=\"openConfirm('danger')\">\n Confirm a destructive action\n </button>\n <button matButton=\"outlined\" color=\"warn\" type=\"button\" (click)=\"openConfirm('reason')\">\n Confirm with a reason\n </button>\n </div>\n <rb-message [tone]=\"dialogResultTone()\" title=\"Last result\">{{ dialogResult() }}</rb-message>\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-classes\">\n <h2 id=\"showcase-classes\" class=\"showcase__heading\">Style classes</h2>\n\n <h3 class=\"showcase__subheading\">Forms</h3>\n <form class=\"rb-form-grid\" (submit)=\"$event.preventDefault()\">\n <div class=\"rb-field\">\n <label class=\"rb-field__label\" for=\"showcase-field-name\">Application name</label>\n <input\n id=\"showcase-field-name\"\n class=\"rb-field__control\"\n type=\"text\"\n value=\"Photogrammetry workspace\"\n aria-describedby=\"showcase-field-name-hint\"\n />\n <p id=\"showcase-field-name-hint\" class=\"rb-field__hint\">Shown on the consent screen.</p>\n </div>\n <div class=\"rb-field\">\n <label class=\"rb-field__label\" for=\"showcase-field-environment\">Environment</label>\n <select id=\"showcase-field-environment\" class=\"rb-field__control\">\n <option>Production</option>\n <option>Staging</option>\n </select>\n </div>\n <div class=\"rb-field rb-field--invalid\">\n <label class=\"rb-field__label\" for=\"showcase-field-uri\">Redirect URI</label>\n <input\n id=\"showcase-field-uri\"\n class=\"rb-field__control\"\n type=\"url\"\n value=\"http://photogrammetry\"\n aria-invalid=\"true\"\n aria-describedby=\"showcase-field-uri-error\"\n />\n <p id=\"showcase-field-uri-error\" class=\"rb-field__error\">\n Enter an https:// address, for example https://app.rblabs.dev/callback.\n </p>\n </div>\n <div class=\"rb-field rb-field--disabled\">\n <label class=\"rb-field__label\" for=\"showcase-field-client\">Client ID</label>\n <input\n id=\"showcase-field-client\"\n class=\"rb-field__control\"\n type=\"text\"\n value=\"app_01J2B7F9P4L6X3V8S1D5G0HAKM\"\n disabled\n aria-describedby=\"showcase-field-client-hint\"\n />\n <p id=\"showcase-field-client-hint\" class=\"rb-field__hint\">\n Generated at registration and cannot be changed.\n </p>\n </div>\n <div class=\"rb-field rb-form-grid__full\">\n <label class=\"rb-field__label\" for=\"showcase-field-notes\">Notes</label>\n <textarea\n id=\"showcase-field-notes\"\n class=\"rb-field__control\"\n placeholder=\"Optional context for other administrators\"\n ></textarea>\n </div>\n <mat-form-field appearance=\"outline\">\n <mat-label>Display name (mat-form-field)</mat-label>\n <input matInput value=\"Photogrammetry\" />\n <mat-hint>Material fields sit in the same grid.</mat-hint>\n </mat-form-field>\n <div class=\"rb-field rb-form-grid__full\">\n <span id=\"showcase-field-scopes\" class=\"rb-field__label\"\n >Granted scopes (mat-chip-grid)</span\n >\n @if (scopes().length > 0) {\n <mat-chip-grid aria-labelledby=\"showcase-field-scopes\">\n @for (scope of scopes(); track scope) {\n <mat-chip-row (removed)=\"removeScope(scope)\">\n {{ scope }}\n <button matChipRemove [attr.aria-label]=\"'Remove ' + scope\">\n <mat-icon aria-hidden=\"true\">close</mat-icon>\n </button>\n </mat-chip-row>\n }\n </mat-chip-grid>\n } @else {\n <p class=\"rb-field__hint\">No scopes granted.</p>\n }\n <p class=\"rb-field__hint\">\n Remove buttons are at least 24px on fine pointers and 44px on coarse pointers.\n @if (scopes().length < defaultScopes.length) {\n <button matButton type=\"button\" (click)=\"resetScopes()\">Restore scopes</button>\n }\n </p>\n </div>\n </form>\n\n <h3 class=\"showcase__subheading\">Tables</h3>\n <p class=\"showcase__lead\">\n Below the <code>md</code> breakpoint each row becomes a stacked record labelled from\n <code>data-label</code>.\n </p>\n <div class=\"rb-table-scroll\">\n <table class=\"rb-table\">\n <caption class=\"rb-visually-hidden\">\n Registered applications\n </caption>\n <thead>\n <tr>\n <th scope=\"col\">Name</th>\n <th scope=\"col\">Client ID</th>\n <th scope=\"col\">Status</th>\n <th scope=\"col\">Updated</th>\n <th scope=\"col\"><span class=\"rb-visually-hidden\">Actions</span></th>\n </tr>\n </thead>\n <tbody>\n @for (row of tableRows; track row.clientId) {\n <tr>\n <td data-label=\"Name\">{{ row.name }}</td>\n <td data-label=\"Client ID\" class=\"showcase__cell-id\">\n <rb-technical-value [value]=\"row.clientId\" />\n </td>\n <td data-label=\"Status\">\n <rb-status-chip\n [tone]=\"row.status.tone\"\n [label]=\"row.status.label\"\n size=\"compact\"\n />\n </td>\n <td data-label=\"Updated\">{{ row.updated }}</td>\n <td>\n <div class=\"rb-table__actions\">\n <button\n matIconButton\n type=\"button\"\n [attr.aria-label]=\"'Edit ' + row.name\"\n [matTooltip]=\"'Edit ' + row.name\"\n >\n <mat-icon aria-hidden=\"true\">edit</mat-icon>\n </button>\n <button\n matIconButton\n color=\"warn\"\n type=\"button\"\n [attr.aria-label]=\"'Delete ' + row.name\"\n [matTooltip]=\"'Delete ' + row.name\"\n >\n <mat-icon aria-hidden=\"true\">delete</mat-icon>\n </button>\n </div>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <div class=\"showcase__grid\">\n <div class=\"rb-table-scroll\">\n <table class=\"rb-table\" aria-busy=\"true\">\n <caption class=\"rb-visually-hidden\">\n Applications, loading\n </caption>\n <thead>\n <tr>\n <th scope=\"col\">Name</th>\n <th scope=\"col\">Status</th>\n </tr>\n </thead>\n <tbody>\n @for (row of [1, 2, 3]; track row) {\n <tr class=\"rb-table__skeleton-row\" aria-hidden=\"true\">\n <td><span class=\"rb-skeleton\"></span></td>\n <td><span class=\"rb-skeleton\"></span></td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n <div class=\"rb-table-scroll\">\n <table class=\"rb-table\">\n <caption class=\"rb-visually-hidden\">\n Applications matching “rover”\n </caption>\n <thead>\n <tr>\n <th scope=\"col\">Name</th>\n <th scope=\"col\">Status</th>\n </tr>\n </thead>\n <tbody>\n <tr class=\"rb-table__empty-row\">\n <td colspan=\"2\">No applications match “rover”.</td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n\n <p class=\"showcase__lead\">\n <code>mat-table</code> with <code>rb-table--inline</code>: the grid is kept on phones and\n scrolls inside <code>rb-table-scroll</code>, which is focusable so keyboard users can scroll\n it.\n </p>\n <div\n class=\"rb-table-scroll\"\n role=\"region\"\n aria-label=\"Registered applications, scrollable\"\n tabindex=\"0\"\n >\n <table mat-table class=\"rb-table rb-table--inline\" [dataSource]=\"tableRows\">\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef scope=\"col\">Name</th>\n <td mat-cell *matCellDef=\"let row\" data-label=\"Name\">{{ row.name }}</td>\n </ng-container>\n <ng-container matColumnDef=\"clientId\">\n <th mat-header-cell *matHeaderCellDef scope=\"col\">Client ID</th>\n <td mat-cell *matCellDef=\"let row\" data-label=\"Client ID\">\n <rb-technical-value [value]=\"row.clientId\" />\n </td>\n </ng-container>\n <ng-container matColumnDef=\"status\">\n <th mat-header-cell *matHeaderCellDef scope=\"col\">Status</th>\n <td mat-cell *matCellDef=\"let row\" data-label=\"Status\">\n <rb-status-chip [tone]=\"row.status.tone\" [label]=\"row.status.label\" size=\"compact\" />\n </td>\n </ng-container>\n <ng-container matColumnDef=\"updated\">\n <th mat-header-cell *matHeaderCellDef scope=\"col\">Updated</th>\n <td mat-cell *matCellDef=\"let row\" data-label=\"Updated\">{{ row.updated }}</td>\n </ng-container>\n <tr mat-header-row *matHeaderRowDef=\"tableColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: tableColumns\"></tr>\n <tr *matNoDataRow>\n <td [attr.colspan]=\"tableColumns.length\">No applications.</td>\n </tr>\n </table>\n </div>\n\n <h3 class=\"showcase__subheading\">Layout</h3>\n <div class=\"rb-master-detail\">\n <rb-panel\n title=\"rb-master-detail\"\n description=\"List on the left, detail on the right.\"\n variant=\"soft\"\n [headingLevel]=\"4\"\n >\n <div class=\"rb-stack rb-stack--sm\">\n <span class=\"rb-skeleton\" aria-hidden=\"true\"></span>\n <span class=\"rb-skeleton\" aria-hidden=\"true\"></span>\n <span class=\"rb-skeleton\" aria-hidden=\"true\"></span>\n </div>\n </rb-panel>\n <rb-panel title=\"Detail\" description=\"Stacks below the xl breakpoint.\" [headingLevel]=\"4\">\n <div class=\"rb-stack\">\n <dl class=\"rb-meta-grid\">\n <div class=\"rb-meta-grid__item\">\n <dt class=\"rb-meta-grid__label\">Owner</dt>\n <dd class=\"rb-meta-grid__value\">Platform team</dd>\n </div>\n <div class=\"rb-meta-grid__item\">\n <dt class=\"rb-meta-grid__label\">Created</dt>\n <dd class=\"rb-meta-grid__value\">3 Mar 2026</dd>\n </div>\n <div class=\"rb-meta-grid__item\">\n <dt class=\"rb-meta-grid__label\">Scopes</dt>\n <dd class=\"rb-meta-grid__value\">openid profile</dd>\n </div>\n </dl>\n <span class=\"rb-skeleton rb-skeleton--heading\" aria-hidden=\"true\"></span>\n <span class=\"rb-skeleton rb-skeleton--control\" aria-hidden=\"true\"></span>\n <div class=\"rb-action-group rb-action-group--between rb-action-group--mobile-stack\">\n <button matButton color=\"warn\" type=\"button\">Delete</button>\n <div class=\"rb-action-group rb-action-group--end rb-action-group--mobile-stack\">\n <button matButton=\"outlined\" type=\"button\">Cancel</button>\n <button matButton=\"filled\" type=\"button\">Save changes</button>\n </div>\n </div>\n </div>\n </rb-panel>\n </div>\n\n <h3 class=\"showcase__subheading\">Text and landing</h3>\n <p class=\"showcase__lead\">\n The hero, tags and icon tiles of the public home pages, and the two uppercase label roles.\n </p>\n <div class=\"rb-hero\">\n <div class=\"rb-hero__copy\">\n <p class=\"rb-eyebrow\">\n <mat-icon aria-hidden=\"true\">deployed_code</mat-icon>\n rb-eyebrow\n </p>\n <p class=\"rb-hero__title\">One standard for every RB Labs app</p>\n <p class=\"rb-hero__lead\">\n <code>.rb-hero__title</code> uses the hero type role; <code>.rb-hero__lead</code> sits\n under it. Below the lg breakpoint the media column moves under the copy.\n </p>\n <ul class=\"rb-tag-list\" aria-label=\"Stack\">\n <li>Angular</li>\n <li>Angular Material 3</li>\n <li>WCAG 2.2 AA</li>\n </ul>\n </div>\n <rb-panel title=\"Media column\" variant=\"soft\" [headingLevel]=\"4\">\n <ul class=\"showcase__icon-tiles\">\n @for (tile of iconTiles; track tile.label) {\n <li>\n <span class=\"rb-icon-tile\" aria-hidden=\"true\">\n <mat-icon>{{ tile.icon }}</mat-icon>\n </span>\n <span>\n <span class=\"rb-overline\">{{ tile.overline }}</span>\n <a class=\"rb-table__link\" routerLink=\".\" fragment=\"showcase-classes\">{{\n tile.label\n }}</a>\n </span>\n </li>\n }\n </ul>\n </rb-panel>\n </div>\n <div class=\"showcase__stage\">\n <p class=\"showcase__stage-caption\">\n Over a media stage, labels take <code>.rb-overline--on-stage</code>.\n </p>\n <dl class=\"showcase__stage-list\">\n @for (metric of stageMetrics; track metric.label) {\n <div>\n <dt class=\"rb-overline rb-overline--on-stage\">{{ metric.label }}</dt>\n <dd>{{ metric.value }}</dd>\n </div>\n }\n </dl>\n </div>\n </section>\n </main>\n}\n",
704
+ styles: [":host{display:block;min-height:100dvh;padding:var(--rb-space-page)}:host(.showcase--example){padding:0}.showcase__main{width:min(100%,var(--rb-size-content-max));margin-inline:auto}.showcase__header{padding-block-end:var(--rb-space-6);border-block-end:1px solid var(--rb-color-border)}.showcase__lead,.showcase__text{margin:0;color:var(--rb-color-text-muted);font:var(--rb-type-body-medium)}.showcase__lead{max-width:48rem}.showcase__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,18rem),1fr));gap:var(--rb-space-4);align-items:start}.showcase__narrow{max-inline-size:12rem}.showcase__cell-id{max-inline-size:14rem}.showcase__note{margin:0;color:var(--rb-color-text-muted);font:var(--rb-type-body-small)}.showcase__main{display:grid;gap:var(--rb-space-10);padding-block-end:var(--rb-space-8)}.showcase__main:focus{outline:none}.showcase__section{display:grid;gap:var(--rb-space-4);min-width:0}.showcase__heading{margin:0;color:var(--rb-color-text-strong);font:var(--rb-type-title-large)}.showcase__subheading{margin:var(--rb-space-2) 0 0;color:var(--rb-color-text-muted);font:var(--rb-type-title-small)}.showcase__swatches,.showcase__tiles,.showcase__list{margin:0;padding:0;list-style:none}.showcase__swatches{display:grid;grid-template-columns:repeat(auto-fill,minmax(12rem,1fr));gap:var(--rb-space-3)}.showcase__swatch{display:grid;gap:var(--rb-space-1);min-width:0}.showcase__swatch code{overflow-wrap:anywhere;color:var(--rb-color-text);font-size:var(--rb-type-body-small-size)}.showcase__chip{display:grid;height:3.5rem;place-items:center;border:1px solid var(--rb-color-border);border-radius:var(--rb-radius-md);font:var(--rb-type-title-medium)}.showcase__list{display:grid;gap:var(--rb-space-3)}.showcase__type-row,.showcase__space-row{display:grid;grid-template-columns:9rem minmax(0,1fr);gap:var(--rb-space-4);align-items:center}.showcase__type-row code,.showcase__space-row code{color:var(--rb-color-text-subtle);font-size:var(--rb-type-body-small-size)}@media(width<40rem){.showcase__type-row,.showcase__space-row{grid-template-columns:1fr;gap:var(--rb-space-1)}}.showcase__type-sample{overflow-wrap:anywhere;color:var(--rb-color-text-strong)}.showcase__mono{margin:0;color:var(--rb-color-text);font-family:var(--rb-font-mono)}.showcase__space-bar{display:block;height:var(--rb-space-3);border-radius:var(--rb-radius-xs);background-color:var(--rb-color-primary)}.showcase__tiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(8rem,1fr));gap:var(--rb-space-4)}.showcase__tile{display:grid;min-height:5rem;place-items:center;border:1px solid var(--rb-color-border-strong);border-radius:var(--rb-radius-lg);background-color:var(--rb-color-surface);color:var(--rb-color-text-muted);font-size:var(--rb-type-body-small-size)}.showcase__tile--raised{border-color:transparent;background-color:var(--rb-color-surface-raised)}.showcase__brand-row{display:flex;flex-wrap:wrap;gap:var(--rb-space-6);align-items:center}.showcase__brand-row--list{margin:0;padding:0;list-style:none}.showcase__monogram{display:inline-flex;gap:var(--rb-space-2);align-items:center}.showcase__monogram code{color:var(--rb-color-text-subtle);font-size:var(--rb-type-body-small-size)}.showcase__frame{display:block;inline-size:100%;block-size:40rem;border:1px solid var(--rb-color-border);border-radius:var(--rb-radius-lg);background-color:var(--rb-color-bg)}.showcase__icon-tiles{display:grid;gap:var(--rb-space-4);margin:0;padding:0;list-style:none}.showcase__icon-tiles>li{display:flex;gap:var(--rb-space-3);align-items:center}.showcase__icon-tiles>li>span:last-child{display:grid;gap:var(--rb-space-1)}.showcase__stage{display:grid;gap:var(--rb-space-4);margin-block-start:var(--rb-space-6);padding:var(--rb-space-6);border-radius:var(--rb-radius-lg);background-color:var(--rb-color-stage);color:var(--rb-color-on-stage)}.showcase__stage-caption{margin:0;font:var(--rb-type-body-medium)}.showcase__stage-caption code{font-size:var(--rb-type-body-small-size)}.showcase__stage-list{display:flex;flex-wrap:wrap;gap:var(--rb-space-2) var(--rb-space-8);margin:0}.showcase__stage-list dd{margin:0;font:var(--rb-type-body-medium);font-variant-numeric:tabular-nums}\n"],
690
705
  dependencies: [
691
706
  {
692
707
  kind: "component",
@@ -1017,8 +1032,8 @@ i0.ɵɵngDeclareClassMetadata({
1017
1032
  class: "rb-scope",
1018
1033
  "[class.showcase--example]": "example() !== null"
1019
1034
  },
1020
- template: "@if (example(); as kind) {\n <rb-showcase-shell-example [kind]=\"kind\" />\n} @else {\n <!-- Focuses main itself: under <base href=\"/\"> the bare fragment would load the root route. -->\n <a\n class=\"rb-skip-link\"\n href=\"#rb-showcase-main\"\n (click)=\"$event.preventDefault(); showcaseMain.focus()\"\n >Skip to main content</a\n >\n\n <main\n #showcaseMain\n id=\"rb-showcase-main\"\n class=\"showcase__main\"\n tabindex=\"-1\"\n aria-label=\"Design system reference\"\n >\n <rb-page-header\n class=\"showcase__header\"\n eyebrow=\"RB Labs Design System\"\n title=\"Foundations and components\"\n [description]=\"\n theme.preference() === null\n ? 'Tokens, Material components and the content kit, following the operating system theme.'\n : 'Tokens, Material components and the content kit.'\n \"\n [metrics]=\"headerMetrics()\"\n >\n <rb-theme-toggle rbActions />\n </rb-page-header>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-color\">\n <h2 id=\"showcase-color\" class=\"showcase__heading\">Color roles</h2>\n @for (group of colorGroups; track group.title) {\n <h3 class=\"showcase__subheading\">{{ group.title }}</h3>\n <ul class=\"showcase__swatches\">\n @for (role of group.roles; track role.token) {\n <li class=\"showcase__swatch\">\n <span\n class=\"showcase__chip\"\n [style.background-color]=\"'var(--rb-color-' + role.token + ')'\"\n [style.color]=\"role.on ? 'var(--rb-color-' + role.on + ')' : null\"\n aria-hidden=\"true\"\n >{{ role.on ? 'Aa' : '' }}</span\n >\n <code>--rb-color-{{ role.token }}</code>\n @if (role.on) {\n <span class=\"showcase__note\">text: {{ role.on }}</span>\n }\n </li>\n }\n </ul>\n }\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-type\">\n <h2 id=\"showcase-type\" class=\"showcase__heading\">Type scale</h2>\n <ul class=\"showcase__list\">\n @for (role of typeRoles; track role) {\n <li class=\"showcase__type-row\">\n <code>{{ role }}</code>\n <span\n class=\"showcase__type-sample\"\n [style.font]=\"'var(--rb-type-' + role + ')'\"\n [style.letter-spacing]=\"'var(--rb-type-' + role + '-tracking)'\"\n >Operate RB Labs</span\n >\n </li>\n }\n </ul>\n <p class=\"showcase__mono\">ui-monospace · usr_01HZX9 · sha256:2b4f69c9</p>\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-space\">\n <h2 id=\"showcase-space\" class=\"showcase__heading\">Spacing</h2>\n <ul class=\"showcase__list\">\n @for (step of spaceSteps; track step) {\n <li class=\"showcase__space-row\">\n <code>--rb-space-{{ step }}</code>\n <span\n class=\"showcase__space-bar\"\n [style.inline-size]=\"'var(--rb-space-' + step + ')'\"\n aria-hidden=\"true\"\n ></span>\n </li>\n }\n </ul>\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-shape\">\n <h2 id=\"showcase-shape\" class=\"showcase__heading\">Radius and elevation</h2>\n <ul class=\"showcase__tiles\">\n @for (radius of radii; track radius) {\n <li class=\"showcase__tile\" [style.border-radius]=\"'var(--rb-radius-' + radius + ')'\">\n <code>radius-{{ radius }}</code>\n </li>\n }\n </ul>\n <ul class=\"showcase__tiles\">\n @for (level of elevations; track level) {\n <li\n class=\"showcase__tile showcase__tile--raised\"\n [style.box-shadow]=\"'var(--rb-elevation-' + level + ')'\"\n >\n <code>elevation-{{ level }}</code>\n </li>\n }\n </ul>\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-material\">\n <h2 id=\"showcase-material\" class=\"showcase__heading\">Buttons</h2>\n <p class=\"showcase__lead\">\n There is no kit button component. Each role maps to an Angular Material button variant.\n </p>\n <div class=\"rb-table-scroll\">\n <table class=\"rb-table\">\n <caption class=\"rb-visually-hidden\">\n Button roles and their Material markup\n </caption>\n <thead>\n <tr>\n <th scope=\"col\">Role</th>\n <th scope=\"col\">Markup</th>\n <th scope=\"col\">Equivalent selector</th>\n </tr>\n </thead>\n <tbody>\n @for (button of buttonRoles; track button.role) {\n <tr>\n <td data-label=\"Role\">{{ button.role }}</td>\n <td data-label=\"Markup\">\n <code>{{ button.markup }}</code>\n </td>\n <td data-label=\"Equivalent selector\">\n <code>{{ button.legacy }}</code>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n <div class=\"rb-action-group\">\n <button matButton=\"filled\" type=\"button\">Primary</button>\n <button matButton=\"outlined\" type=\"button\">Secondary</button>\n <button matButton type=\"button\">Tertiary</button>\n <button matButton=\"tonal\" type=\"button\">Tonal</button>\n <button matButton=\"filled\" type=\"button\" disabled>Disabled</button>\n </div>\n <div class=\"rb-action-group\">\n <button matButton=\"filled\" color=\"warn\" type=\"button\">\n <mat-icon aria-hidden=\"true\">delete</mat-icon>\n Delete\n </button>\n <button matButton=\"outlined\" color=\"warn\" type=\"button\">Revoke</button>\n <button matButton color=\"warn\" type=\"button\">Remove</button>\n <button\n matIconButton\n color=\"warn\"\n type=\"button\"\n aria-label=\"Delete item\"\n matTooltip=\"Delete item\"\n >\n <mat-icon aria-hidden=\"true\">delete</mat-icon>\n </button>\n <button matIconButton type=\"button\" aria-label=\"Edit item\" matTooltip=\"Edit item\">\n <mat-icon aria-hidden=\"true\">edit</mat-icon>\n </button>\n </div>\n <p class=\"showcase__lead\">\n A link that navigates uses the same variants on an <code>&lt;a&gt;</code>. On coarse\n pointers it reaches the touch target like a button.\n </p>\n <div class=\"rb-action-group\">\n <a matButton=\"filled\" routerLink=\".\" fragment=\"showcase-material\">Primary link</a>\n <a matButton=\"outlined\" routerLink=\".\" fragment=\"showcase-material\">Secondary link</a>\n <a matButton routerLink=\".\" fragment=\"showcase-material\">Tertiary link</a>\n <a matButton=\"tonal\" routerLink=\".\" fragment=\"showcase-material\">Tonal link</a>\n <a\n matIconButton\n routerLink=\".\"\n fragment=\"showcase-material\"\n aria-label=\"Go to buttons\"\n matTooltip=\"Go to buttons\"\n >\n <mat-icon aria-hidden=\"true\">arrow_forward</mat-icon>\n </a>\n </div>\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-shells\">\n <h2 id=\"showcase-shells\" class=\"showcase__heading\">Shells and brand</h2>\n\n <h3 class=\"showcase__subheading\">Brand lockup and monogram</h3>\n <div class=\"showcase__brand-row\">\n <rb-brand-lockup appName=\"Admin\" link=\"/design-system\" />\n <rb-brand-lockup appName=\"Photogrammetry\" link=\"/design-system\" />\n <rb-brand-lockup appName=\"Rover\" link=\"/design-system\" compact />\n </div>\n <ul class=\"showcase__brand-row showcase__brand-row--list\" aria-label=\"Monogram sizes\">\n @for (size of monogramSizes; track size.token) {\n <li class=\"showcase__monogram\">\n <rb-monogram [style.inline-size]=\"size.value\" />\n <code>{{ size.token }}</code>\n </li>\n }\n </ul>\n\n <p class=\"showcase__lead\">\n Each preview is a frame whose width is the viewport, so on narrow screens the sidenav\n becomes a drawer and the module nav a tab bar. The previews are inert, so their landmarks do\n not repeat in this page; open the full page to use an example.\n </p>\n @for (example of shellExamples; track example.kind + theme.theme()) {\n <h3 class=\"showcase__subheading\">{{ example.title }}</h3>\n <p class=\"showcase__lead\">\n {{ example.description }}\n <a routerLink=\".\" [queryParams]=\"{ example: example.kind }\">Open full page</a>\n </p>\n <iframe\n class=\"showcase__frame\"\n [title]=\"example.title + ' example'\"\n [src]=\"example.src\"\n loading=\"lazy\"\n inert\n ></iframe>\n }\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-components\">\n <h2 id=\"showcase-components\" class=\"showcase__heading\">Content components</h2>\n\n <h3 class=\"showcase__subheading\">Page header</h3>\n <p class=\"showcase__lead\">\n The header of this page is an <code>rb-page-header</code> with an eyebrow, description,\n metrics and the theme toggle as its action. A page has exactly one.\n </p>\n\n <h3 class=\"showcase__subheading\">Panel</h3>\n <div class=\"showcase__grid\">\n <rb-panel title=\"Default\" description=\"Cards and grouped content.\" [headingLevel]=\"4\">\n <button rbActions matButton type=\"button\">\n <mat-icon aria-hidden=\"true\">refresh</mat-icon>\n Refresh\n </button>\n <p class=\"showcase__text\">Surface background, border and elevation 1.</p>\n </rb-panel>\n <rb-panel\n title=\"Soft\"\n description=\"Secondary or inset content.\"\n variant=\"soft\"\n [headingLevel]=\"4\"\n >\n <p class=\"showcase__text\">Sunken surface, no shadow.</p>\n </rb-panel>\n <rb-panel\n title=\"Elevated\"\n description=\"Content that floats above the page.\"\n variant=\"elevated\"\n [headingLevel]=\"4\"\n >\n <p class=\"showcase__text\">Raised surface and elevation 2.</p>\n </rb-panel>\n <rb-panel\n title=\"Danger zone\"\n description=\"Destructive settings.\"\n variant=\"danger\"\n [headingLevel]=\"4\"\n >\n <button rbActions matButton=\"outlined\" color=\"warn\" type=\"button\">Delete</button>\n <p class=\"showcase__text\">Danger border and title; the title names the risk.</p>\n </rb-panel>\n </div>\n\n <h3 class=\"showcase__subheading\">Message</h3>\n <div class=\"rb-stack rb-stack--sm\">\n @for (sample of messageSamples; track sample.tone) {\n <rb-message [tone]=\"sample.tone\" [title]=\"sample.title\" live=\"off\">\n {{ sample.body }}\n </rb-message>\n }\n </div>\n\n <h3 class=\"showcase__subheading\">Empty state</h3>\n <div class=\"showcase__grid\">\n <rb-panel title=\"Applications\" [headingLevel]=\"4\">\n <rb-empty-state\n icon=\"apps\"\n title=\"No applications yet\"\n description=\"Register a client to issue credentials for RB Labs sign-in.\"\n actionLabel=\"Register application\"\n [headingLevel]=\"5\"\n />\n </rb-panel>\n <rb-panel title=\"Audit history\" variant=\"soft\" [headingLevel]=\"4\">\n <rb-empty-state\n icon=\"history\"\n title=\"Nothing recorded\"\n description=\"Changes to this application appear here.\"\n layout=\"inline\"\n [headingLevel]=\"5\"\n />\n </rb-panel>\n </div>\n\n <h3 class=\"showcase__subheading\">Status chip</h3>\n <div class=\"rb-action-group\">\n @for (chip of chipSamples; track chip.tone) {\n <rb-status-chip [tone]=\"chip.tone\" [label]=\"chip.label\" />\n }\n </div>\n <div class=\"rb-action-group\">\n @for (chip of chipSamples; track chip.tone) {\n <rb-status-chip [tone]=\"chip.tone\" [label]=\"chip.label\" size=\"compact\" />\n }\n <rb-status-chip label=\"Locked\" icon=\"lock\" size=\"compact\" />\n </div>\n\n <h3 class=\"showcase__subheading\">Technical value</h3>\n <dl class=\"rb-meta-grid\">\n <div class=\"rb-meta-grid__item\">\n <dt class=\"rb-meta-grid__label\">Fits</dt>\n <dd class=\"rb-meta-grid__value\"><rb-technical-value value=\"usr_01HZX9\" /></dd>\n </div>\n <div class=\"rb-meta-grid__item showcase__narrow\">\n <dt class=\"rb-meta-grid__label\">Truncated</dt>\n <dd class=\"rb-meta-grid__value\">\n <rb-technical-value\n value=\"https://auth.rblabs.dev/oauth/callback/photogrammetry-workspace\"\n />\n </dd>\n </div>\n <div class=\"rb-meta-grid__item\">\n <dt class=\"rb-meta-grid__label\">Shortened</dt>\n <dd class=\"rb-meta-grid__value\">\n <rb-technical-value\n value=\"sha256:2b4f69c9d1e0a7b35c8e6f41a9d2c7b0e3f5a8c1d4b7e0f2a5c8d1e4b7a0c3f6\"\n displayValue=\"sha256:2b4f69c9…\"\n />\n </dd>\n </div>\n </dl>\n\n <h3 class=\"showcase__subheading\">Confirmation dialog</h3>\n <div class=\"rb-action-group\">\n <button matButton=\"outlined\" type=\"button\" (click)=\"openConfirm('primary')\">\n Confirm an action\n </button>\n <button matButton=\"outlined\" color=\"warn\" type=\"button\" (click)=\"openConfirm('danger')\">\n Confirm a destructive action\n </button>\n <button matButton=\"outlined\" color=\"warn\" type=\"button\" (click)=\"openConfirm('reason')\">\n Confirm with a reason\n </button>\n </div>\n <rb-message [tone]=\"dialogResultTone()\" title=\"Last result\">{{ dialogResult() }}</rb-message>\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-classes\">\n <h2 id=\"showcase-classes\" class=\"showcase__heading\">Style classes</h2>\n\n <h3 class=\"showcase__subheading\">Forms</h3>\n <form class=\"rb-form-grid\" (submit)=\"$event.preventDefault()\">\n <div class=\"rb-field\">\n <label class=\"rb-field__label\" for=\"showcase-field-name\">Application name</label>\n <input\n id=\"showcase-field-name\"\n class=\"rb-field__control\"\n type=\"text\"\n value=\"Photogrammetry workspace\"\n aria-describedby=\"showcase-field-name-hint\"\n />\n <p id=\"showcase-field-name-hint\" class=\"rb-field__hint\">Shown on the consent screen.</p>\n </div>\n <div class=\"rb-field\">\n <label class=\"rb-field__label\" for=\"showcase-field-environment\">Environment</label>\n <select id=\"showcase-field-environment\" class=\"rb-field__control\">\n <option>Production</option>\n <option>Staging</option>\n </select>\n </div>\n <div class=\"rb-field rb-field--invalid\">\n <label class=\"rb-field__label\" for=\"showcase-field-uri\">Redirect URI</label>\n <input\n id=\"showcase-field-uri\"\n class=\"rb-field__control\"\n type=\"url\"\n value=\"http://photogrammetry\"\n aria-invalid=\"true\"\n aria-describedby=\"showcase-field-uri-error\"\n />\n <p id=\"showcase-field-uri-error\" class=\"rb-field__error\">\n Enter an https:// address, for example https://app.rblabs.dev/callback.\n </p>\n </div>\n <div class=\"rb-field rb-field--disabled\">\n <label class=\"rb-field__label\" for=\"showcase-field-client\">Client ID</label>\n <input\n id=\"showcase-field-client\"\n class=\"rb-field__control\"\n type=\"text\"\n value=\"app_01J2B7F9P4L6X3V8S1D5G0HAKM\"\n disabled\n aria-describedby=\"showcase-field-client-hint\"\n />\n <p id=\"showcase-field-client-hint\" class=\"rb-field__hint\">\n Generated at registration and cannot be changed.\n </p>\n </div>\n <div class=\"rb-field rb-form-grid__full\">\n <label class=\"rb-field__label\" for=\"showcase-field-notes\">Notes</label>\n <textarea\n id=\"showcase-field-notes\"\n class=\"rb-field__control\"\n placeholder=\"Optional context for other administrators\"\n ></textarea>\n </div>\n <mat-form-field appearance=\"outline\">\n <mat-label>Display name (mat-form-field)</mat-label>\n <input matInput value=\"Photogrammetry\" />\n <mat-hint>Material fields sit in the same grid.</mat-hint>\n </mat-form-field>\n <div class=\"rb-field rb-form-grid__full\">\n <span id=\"showcase-field-scopes\" class=\"rb-field__label\"\n >Granted scopes (mat-chip-grid)</span\n >\n @if (scopes().length > 0) {\n <mat-chip-grid aria-labelledby=\"showcase-field-scopes\">\n @for (scope of scopes(); track scope) {\n <mat-chip-row (removed)=\"removeScope(scope)\">\n {{ scope }}\n <button matChipRemove [attr.aria-label]=\"'Remove ' + scope\">\n <mat-icon aria-hidden=\"true\">close</mat-icon>\n </button>\n </mat-chip-row>\n }\n </mat-chip-grid>\n } @else {\n <p class=\"rb-field__hint\">No scopes granted.</p>\n }\n <p class=\"rb-field__hint\">\n Remove buttons are at least 24px on fine pointers and 44px on coarse pointers.\n @if (scopes().length < defaultScopes.length) {\n <button matButton type=\"button\" (click)=\"resetScopes()\">Restore scopes</button>\n }\n </p>\n </div>\n </form>\n\n <h3 class=\"showcase__subheading\">Tables</h3>\n <p class=\"showcase__lead\">\n Below the <code>md</code> breakpoint each row becomes a stacked record labelled from\n <code>data-label</code>.\n </p>\n <div class=\"rb-table-scroll\">\n <table class=\"rb-table\">\n <caption class=\"rb-visually-hidden\">\n Registered applications\n </caption>\n <thead>\n <tr>\n <th scope=\"col\">Name</th>\n <th scope=\"col\">Client ID</th>\n <th scope=\"col\">Status</th>\n <th scope=\"col\">Updated</th>\n <th scope=\"col\"><span class=\"rb-visually-hidden\">Actions</span></th>\n </tr>\n </thead>\n <tbody>\n @for (row of tableRows; track row.clientId) {\n <tr>\n <td data-label=\"Name\">{{ row.name }}</td>\n <td data-label=\"Client ID\" class=\"showcase__cell-id\">\n <rb-technical-value [value]=\"row.clientId\" />\n </td>\n <td data-label=\"Status\">\n <rb-status-chip\n [tone]=\"row.status.tone\"\n [label]=\"row.status.label\"\n size=\"compact\"\n />\n </td>\n <td data-label=\"Updated\">{{ row.updated }}</td>\n <td>\n <div class=\"rb-table__actions\">\n <button\n matIconButton\n type=\"button\"\n [attr.aria-label]=\"'Edit ' + row.name\"\n [matTooltip]=\"'Edit ' + row.name\"\n >\n <mat-icon aria-hidden=\"true\">edit</mat-icon>\n </button>\n <button\n matIconButton\n color=\"warn\"\n type=\"button\"\n [attr.aria-label]=\"'Delete ' + row.name\"\n [matTooltip]=\"'Delete ' + row.name\"\n >\n <mat-icon aria-hidden=\"true\">delete</mat-icon>\n </button>\n </div>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <div class=\"showcase__grid\">\n <div class=\"rb-table-scroll\">\n <table class=\"rb-table\" aria-busy=\"true\">\n <caption class=\"rb-visually-hidden\">\n Applications, loading\n </caption>\n <thead>\n <tr>\n <th scope=\"col\">Name</th>\n <th scope=\"col\">Status</th>\n </tr>\n </thead>\n <tbody>\n @for (row of [1, 2, 3]; track row) {\n <tr class=\"rb-table__skeleton-row\" aria-hidden=\"true\">\n <td><span class=\"rb-skeleton\"></span></td>\n <td><span class=\"rb-skeleton\"></span></td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n <div class=\"rb-table-scroll\">\n <table class=\"rb-table\">\n <caption class=\"rb-visually-hidden\">\n Applications matching “rover”\n </caption>\n <thead>\n <tr>\n <th scope=\"col\">Name</th>\n <th scope=\"col\">Status</th>\n </tr>\n </thead>\n <tbody>\n <tr class=\"rb-table__empty-row\">\n <td colspan=\"2\">No applications match “rover”.</td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n\n <p class=\"showcase__lead\">\n <code>mat-table</code> with <code>rb-table--inline</code>: the grid is kept on phones and\n scrolls inside <code>rb-table-scroll</code>, which is focusable so keyboard users can scroll\n it.\n </p>\n <div\n class=\"rb-table-scroll\"\n role=\"region\"\n aria-label=\"Registered applications, scrollable\"\n tabindex=\"0\"\n >\n <table mat-table class=\"rb-table rb-table--inline\" [dataSource]=\"tableRows\">\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef scope=\"col\">Name</th>\n <td mat-cell *matCellDef=\"let row\" data-label=\"Name\">{{ row.name }}</td>\n </ng-container>\n <ng-container matColumnDef=\"clientId\">\n <th mat-header-cell *matHeaderCellDef scope=\"col\">Client ID</th>\n <td mat-cell *matCellDef=\"let row\" data-label=\"Client ID\">\n <rb-technical-value [value]=\"row.clientId\" />\n </td>\n </ng-container>\n <ng-container matColumnDef=\"status\">\n <th mat-header-cell *matHeaderCellDef scope=\"col\">Status</th>\n <td mat-cell *matCellDef=\"let row\" data-label=\"Status\">\n <rb-status-chip [tone]=\"row.status.tone\" [label]=\"row.status.label\" size=\"compact\" />\n </td>\n </ng-container>\n <ng-container matColumnDef=\"updated\">\n <th mat-header-cell *matHeaderCellDef scope=\"col\">Updated</th>\n <td mat-cell *matCellDef=\"let row\" data-label=\"Updated\">{{ row.updated }}</td>\n </ng-container>\n <tr mat-header-row *matHeaderRowDef=\"tableColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: tableColumns\"></tr>\n <tr *matNoDataRow>\n <td [attr.colspan]=\"tableColumns.length\">No applications.</td>\n </tr>\n </table>\n </div>\n\n <h3 class=\"showcase__subheading\">Layout</h3>\n <div class=\"rb-master-detail\">\n <rb-panel\n title=\"rb-master-detail\"\n description=\"List on the left, detail on the right.\"\n variant=\"soft\"\n [headingLevel]=\"4\"\n >\n <div class=\"rb-stack rb-stack--sm\">\n <span class=\"rb-skeleton\" aria-hidden=\"true\"></span>\n <span class=\"rb-skeleton\" aria-hidden=\"true\"></span>\n <span class=\"rb-skeleton\" aria-hidden=\"true\"></span>\n </div>\n </rb-panel>\n <rb-panel title=\"Detail\" description=\"Stacks below the xl breakpoint.\" [headingLevel]=\"4\">\n <div class=\"rb-stack\">\n <dl class=\"rb-meta-grid\">\n <div class=\"rb-meta-grid__item\">\n <dt class=\"rb-meta-grid__label\">Owner</dt>\n <dd class=\"rb-meta-grid__value\">Platform team</dd>\n </div>\n <div class=\"rb-meta-grid__item\">\n <dt class=\"rb-meta-grid__label\">Created</dt>\n <dd class=\"rb-meta-grid__value\">3 Mar 2026</dd>\n </div>\n <div class=\"rb-meta-grid__item\">\n <dt class=\"rb-meta-grid__label\">Scopes</dt>\n <dd class=\"rb-meta-grid__value\">openid profile</dd>\n </div>\n </dl>\n <span class=\"rb-skeleton rb-skeleton--heading\" aria-hidden=\"true\"></span>\n <span class=\"rb-skeleton rb-skeleton--control\" aria-hidden=\"true\"></span>\n <div class=\"rb-action-group rb-action-group--between rb-action-group--mobile-stack\">\n <button matButton color=\"warn\" type=\"button\">Delete</button>\n <div class=\"rb-action-group rb-action-group--end rb-action-group--mobile-stack\">\n <button matButton=\"outlined\" type=\"button\">Cancel</button>\n <button matButton=\"filled\" type=\"button\">Save changes</button>\n </div>\n </div>\n </div>\n </rb-panel>\n </div>\n\n <h3 class=\"showcase__subheading\">Text and landing</h3>\n <p class=\"showcase__lead\">\n The hero, tags and icon tiles of the public home pages, and the two uppercase label roles.\n </p>\n <div class=\"rb-hero\">\n <div class=\"rb-hero__copy\">\n <p class=\"rb-eyebrow\">\n <mat-icon aria-hidden=\"true\">deployed_code</mat-icon>\n rb-eyebrow\n </p>\n <p class=\"rb-hero__title\">One standard for every RB Labs app</p>\n <p class=\"rb-hero__lead\">\n <code>.rb-hero__title</code> uses the hero type role; <code>.rb-hero__lead</code> sits\n under it. Below the lg breakpoint the media column moves under the copy.\n </p>\n <ul class=\"rb-tag-list\" aria-label=\"Stack\">\n <li>Angular</li>\n <li>Angular Material 3</li>\n <li>WCAG 2.2 AA</li>\n </ul>\n </div>\n <rb-panel title=\"Media column\" variant=\"soft\" [headingLevel]=\"4\">\n <ul class=\"showcase__icon-tiles\">\n @for (tile of iconTiles; track tile.label) {\n <li>\n <span class=\"rb-icon-tile\" aria-hidden=\"true\">\n <mat-icon>{{ tile.icon }}</mat-icon>\n </span>\n <span>\n <span class=\"rb-overline\">{{ tile.overline }}</span>\n <a class=\"rb-table__link\" routerLink=\".\" fragment=\"showcase-classes\">{{\n tile.label\n }}</a>\n </span>\n </li>\n }\n </ul>\n </rb-panel>\n </div>\n </section>\n </main>\n}\n",
1021
- styles: [":host{display:block;min-height:100dvh;padding:var(--rb-space-page)}:host(.showcase--example){padding:0}.showcase__main{width:min(100%,var(--rb-size-content-max));margin-inline:auto}.showcase__header{padding-block-end:var(--rb-space-6);border-block-end:1px solid var(--rb-color-border)}.showcase__lead,.showcase__text{margin:0;color:var(--rb-color-text-muted);font:var(--rb-type-body-medium)}.showcase__lead{max-width:48rem}.showcase__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,18rem),1fr));gap:var(--rb-space-4);align-items:start}.showcase__narrow{max-inline-size:12rem}.showcase__cell-id{max-inline-size:14rem}.showcase__note{margin:0;color:var(--rb-color-text-muted);font:var(--rb-type-body-small)}.showcase__main{display:grid;gap:var(--rb-space-10);padding-block-end:var(--rb-space-8)}.showcase__main:focus{outline:none}.showcase__section{display:grid;gap:var(--rb-space-4);min-width:0}.showcase__heading{margin:0;color:var(--rb-color-text-strong);font:var(--rb-type-title-large)}.showcase__subheading{margin:var(--rb-space-2) 0 0;color:var(--rb-color-text-muted);font:var(--rb-type-title-small)}.showcase__swatches,.showcase__tiles,.showcase__list{margin:0;padding:0;list-style:none}.showcase__swatches{display:grid;grid-template-columns:repeat(auto-fill,minmax(12rem,1fr));gap:var(--rb-space-3)}.showcase__swatch{display:grid;gap:var(--rb-space-1);min-width:0}.showcase__swatch code{overflow-wrap:anywhere;color:var(--rb-color-text);font-size:var(--rb-type-body-small-size)}.showcase__chip{display:grid;height:3.5rem;place-items:center;border:1px solid var(--rb-color-border);border-radius:var(--rb-radius-md);font:var(--rb-type-title-medium)}.showcase__list{display:grid;gap:var(--rb-space-3)}.showcase__type-row,.showcase__space-row{display:grid;grid-template-columns:9rem minmax(0,1fr);gap:var(--rb-space-4);align-items:center}.showcase__type-row code,.showcase__space-row code{color:var(--rb-color-text-subtle);font-size:var(--rb-type-body-small-size)}@media(width<40rem){.showcase__type-row,.showcase__space-row{grid-template-columns:1fr;gap:var(--rb-space-1)}}.showcase__type-sample{overflow-wrap:anywhere;color:var(--rb-color-text-strong)}.showcase__mono{margin:0;color:var(--rb-color-text);font-family:var(--rb-font-mono)}.showcase__space-bar{display:block;height:var(--rb-space-3);border-radius:var(--rb-radius-xs);background-color:var(--rb-color-primary)}.showcase__tiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(8rem,1fr));gap:var(--rb-space-4)}.showcase__tile{display:grid;min-height:5rem;place-items:center;border:1px solid var(--rb-color-border-strong);border-radius:var(--rb-radius-lg);background-color:var(--rb-color-surface);color:var(--rb-color-text-muted);font-size:var(--rb-type-body-small-size)}.showcase__tile--raised{border-color:transparent;background-color:var(--rb-color-surface-raised)}.showcase__brand-row{display:flex;flex-wrap:wrap;gap:var(--rb-space-6);align-items:center}.showcase__brand-row--list{margin:0;padding:0;list-style:none}.showcase__monogram{display:inline-flex;gap:var(--rb-space-2);align-items:center}.showcase__monogram code{color:var(--rb-color-text-subtle);font-size:var(--rb-type-body-small-size)}.showcase__frame{display:block;inline-size:100%;block-size:40rem;border:1px solid var(--rb-color-border);border-radius:var(--rb-radius-lg);background-color:var(--rb-color-bg)}.showcase__icon-tiles{display:grid;gap:var(--rb-space-4);margin:0;padding:0;list-style:none}.showcase__icon-tiles>li{display:flex;gap:var(--rb-space-3);align-items:center}.showcase__icon-tiles>li>span:last-child{display:grid;gap:var(--rb-space-1)}\n"]
1035
+ template: "@if (example(); as kind) {\n <rb-showcase-shell-example [kind]=\"kind\" />\n} @else {\n <!-- Focuses main itself: under <base href=\"/\"> the bare fragment would load the root route. -->\n <a\n class=\"rb-skip-link\"\n href=\"#rb-showcase-main\"\n (click)=\"$event.preventDefault(); showcaseMain.focus()\"\n >Skip to main content</a\n >\n\n <main\n #showcaseMain\n id=\"rb-showcase-main\"\n class=\"showcase__main\"\n tabindex=\"-1\"\n aria-label=\"Design system reference\"\n >\n <rb-page-header\n class=\"showcase__header\"\n eyebrow=\"RB Labs Design System\"\n title=\"Foundations and components\"\n [description]=\"\n theme.preference() === null\n ? 'Tokens, Material components and the content kit, following the operating system theme.'\n : 'Tokens, Material components and the content kit.'\n \"\n [metrics]=\"headerMetrics()\"\n >\n <rb-theme-toggle rbActions />\n </rb-page-header>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-color\">\n <h2 id=\"showcase-color\" class=\"showcase__heading\">Color roles</h2>\n @for (group of colorGroups; track group.title) {\n <h3 class=\"showcase__subheading\">{{ group.title }}</h3>\n <ul class=\"showcase__swatches\">\n @for (role of group.roles; track role.token) {\n <li class=\"showcase__swatch\">\n <span\n class=\"showcase__chip\"\n [style.background-color]=\"'var(--rb-color-' + role.token + ')'\"\n [style.color]=\"role.on ? 'var(--rb-color-' + role.on + ')' : null\"\n aria-hidden=\"true\"\n >{{ role.on ? 'Aa' : '' }}</span\n >\n <code>--rb-color-{{ role.token }}</code>\n @if (role.on) {\n <span class=\"showcase__note\">text: {{ role.on }}</span>\n }\n </li>\n }\n </ul>\n }\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-type\">\n <h2 id=\"showcase-type\" class=\"showcase__heading\">Type scale</h2>\n <ul class=\"showcase__list\">\n @for (role of typeRoles; track role) {\n <li class=\"showcase__type-row\">\n <code>{{ role }}</code>\n <span\n class=\"showcase__type-sample\"\n [style.font]=\"'var(--rb-type-' + role + ')'\"\n [style.letter-spacing]=\"'var(--rb-type-' + role + '-tracking)'\"\n >Operate RB Labs</span\n >\n </li>\n }\n </ul>\n <p class=\"showcase__mono\">ui-monospace · usr_01HZX9 · sha256:2b4f69c9</p>\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-space\">\n <h2 id=\"showcase-space\" class=\"showcase__heading\">Spacing</h2>\n <ul class=\"showcase__list\">\n @for (step of spaceSteps; track step) {\n <li class=\"showcase__space-row\">\n <code>--rb-space-{{ step }}</code>\n <span\n class=\"showcase__space-bar\"\n [style.inline-size]=\"'var(--rb-space-' + step + ')'\"\n aria-hidden=\"true\"\n ></span>\n </li>\n }\n </ul>\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-shape\">\n <h2 id=\"showcase-shape\" class=\"showcase__heading\">Radius and elevation</h2>\n <ul class=\"showcase__tiles\">\n @for (radius of radii; track radius) {\n <li class=\"showcase__tile\" [style.border-radius]=\"'var(--rb-radius-' + radius + ')'\">\n <code>radius-{{ radius }}</code>\n </li>\n }\n </ul>\n <ul class=\"showcase__tiles\">\n @for (level of elevations; track level) {\n <li\n class=\"showcase__tile showcase__tile--raised\"\n [style.box-shadow]=\"'var(--rb-elevation-' + level + ')'\"\n >\n <code>elevation-{{ level }}</code>\n </li>\n }\n </ul>\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-material\">\n <h2 id=\"showcase-material\" class=\"showcase__heading\">Buttons</h2>\n <p class=\"showcase__lead\">\n There is no kit button component. Each role maps to an Angular Material button variant.\n </p>\n <div class=\"rb-table-scroll\">\n <table class=\"rb-table\">\n <caption class=\"rb-visually-hidden\">\n Button roles and their Material markup\n </caption>\n <thead>\n <tr>\n <th scope=\"col\">Role</th>\n <th scope=\"col\">Markup</th>\n <th scope=\"col\">Equivalent selector</th>\n </tr>\n </thead>\n <tbody>\n @for (button of buttonRoles; track button.role) {\n <tr>\n <td data-label=\"Role\">{{ button.role }}</td>\n <td data-label=\"Markup\">\n <code>{{ button.markup }}</code>\n </td>\n <td data-label=\"Equivalent selector\">\n <code>{{ button.legacy }}</code>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n <div class=\"rb-action-group\">\n <button matButton=\"filled\" type=\"button\">Primary</button>\n <button matButton=\"outlined\" type=\"button\">Secondary</button>\n <button matButton type=\"button\">Tertiary</button>\n <button matButton=\"tonal\" type=\"button\">Tonal</button>\n <button matButton=\"filled\" type=\"button\" disabled>Disabled</button>\n </div>\n <div class=\"rb-action-group\">\n <button matButton=\"filled\" color=\"warn\" type=\"button\">\n <mat-icon aria-hidden=\"true\">delete</mat-icon>\n Delete\n </button>\n <button matButton=\"outlined\" color=\"warn\" type=\"button\">Revoke</button>\n <button matButton color=\"warn\" type=\"button\">Remove</button>\n <button\n matIconButton\n color=\"warn\"\n type=\"button\"\n aria-label=\"Delete item\"\n matTooltip=\"Delete item\"\n >\n <mat-icon aria-hidden=\"true\">delete</mat-icon>\n </button>\n <button matIconButton type=\"button\" aria-label=\"Edit item\" matTooltip=\"Edit item\">\n <mat-icon aria-hidden=\"true\">edit</mat-icon>\n </button>\n </div>\n <p class=\"showcase__lead\">\n A link that navigates uses the same variants on an <code>&lt;a&gt;</code>. On coarse\n pointers it reaches the touch target like a button.\n </p>\n <div class=\"rb-action-group\">\n <a matButton=\"filled\" routerLink=\".\" fragment=\"showcase-material\">Primary link</a>\n <a matButton=\"outlined\" routerLink=\".\" fragment=\"showcase-material\">Secondary link</a>\n <a matButton routerLink=\".\" fragment=\"showcase-material\">Tertiary link</a>\n <a matButton=\"tonal\" routerLink=\".\" fragment=\"showcase-material\">Tonal link</a>\n <a\n matIconButton\n routerLink=\".\"\n fragment=\"showcase-material\"\n aria-label=\"Go to buttons\"\n matTooltip=\"Go to buttons\"\n >\n <mat-icon aria-hidden=\"true\">arrow_forward</mat-icon>\n </a>\n </div>\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-shells\">\n <h2 id=\"showcase-shells\" class=\"showcase__heading\">Shells and brand</h2>\n\n <h3 class=\"showcase__subheading\">Brand lockup and monogram</h3>\n <div class=\"showcase__brand-row\">\n <rb-brand-lockup appName=\"Admin\" link=\"/design-system\" />\n <rb-brand-lockup appName=\"Photogrammetry\" link=\"/design-system\" />\n <rb-brand-lockup appName=\"Rover\" link=\"/design-system\" compact />\n </div>\n <ul class=\"showcase__brand-row showcase__brand-row--list\" aria-label=\"Monogram sizes\">\n @for (size of monogramSizes; track size.token) {\n <li class=\"showcase__monogram\">\n <rb-monogram [style.inline-size]=\"size.value\" />\n <code>{{ size.token }}</code>\n </li>\n }\n </ul>\n\n <p class=\"showcase__lead\">\n Each preview is a frame whose width is the viewport, so on narrow screens the sidenav\n becomes a drawer and the module nav a tab bar. The previews are inert, so their landmarks do\n not repeat in this page; open the full page to use an example.\n </p>\n @for (example of shellExamples; track example.kind + theme.theme()) {\n <h3 class=\"showcase__subheading\">{{ example.title }}</h3>\n <p class=\"showcase__lead\">\n {{ example.description }}\n <a routerLink=\".\" [queryParams]=\"{ example: example.kind }\">Open full page</a>\n </p>\n <iframe\n class=\"showcase__frame\"\n [title]=\"example.title + ' example'\"\n [src]=\"example.src\"\n loading=\"lazy\"\n inert\n ></iframe>\n }\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-components\">\n <h2 id=\"showcase-components\" class=\"showcase__heading\">Content components</h2>\n\n <h3 class=\"showcase__subheading\">Page header</h3>\n <p class=\"showcase__lead\">\n The header of this page is an <code>rb-page-header</code> with an eyebrow, description,\n metrics and the theme toggle as its action. A page has exactly one.\n </p>\n\n <h3 class=\"showcase__subheading\">Panel</h3>\n <div class=\"showcase__grid\">\n <rb-panel title=\"Default\" description=\"Cards and grouped content.\" [headingLevel]=\"4\">\n <button rbActions matButton type=\"button\">\n <mat-icon aria-hidden=\"true\">refresh</mat-icon>\n Refresh\n </button>\n <p class=\"showcase__text\">Surface background, border and elevation 1.</p>\n </rb-panel>\n <rb-panel\n title=\"Soft\"\n description=\"Secondary or inset content.\"\n variant=\"soft\"\n [headingLevel]=\"4\"\n >\n <p class=\"showcase__text\">Sunken surface, no shadow.</p>\n </rb-panel>\n <rb-panel\n title=\"Elevated\"\n description=\"Content that floats above the page.\"\n variant=\"elevated\"\n [headingLevel]=\"4\"\n >\n <p class=\"showcase__text\">Raised surface and elevation 2.</p>\n </rb-panel>\n <rb-panel\n title=\"Danger zone\"\n description=\"Destructive settings.\"\n variant=\"danger\"\n [headingLevel]=\"4\"\n >\n <button rbActions matButton=\"outlined\" color=\"warn\" type=\"button\">Delete</button>\n <p class=\"showcase__text\">Danger border and title; the title names the risk.</p>\n </rb-panel>\n </div>\n\n <h3 class=\"showcase__subheading\">Message</h3>\n <div class=\"rb-stack rb-stack--sm\">\n @for (sample of messageSamples; track sample.tone) {\n <rb-message [tone]=\"sample.tone\" [title]=\"sample.title\" live=\"off\">\n {{ sample.body }}\n </rb-message>\n }\n </div>\n\n <h3 class=\"showcase__subheading\">Empty state</h3>\n <div class=\"showcase__grid\">\n <rb-panel title=\"Applications\" [headingLevel]=\"4\">\n <rb-empty-state\n icon=\"apps\"\n title=\"No applications yet\"\n description=\"Register a client to issue credentials for RB Labs sign-in.\"\n actionLabel=\"Register application\"\n [headingLevel]=\"5\"\n />\n </rb-panel>\n <rb-panel title=\"Audit history\" variant=\"soft\" [headingLevel]=\"4\">\n <rb-empty-state\n icon=\"history\"\n title=\"Nothing recorded\"\n description=\"Changes to this application appear here.\"\n layout=\"inline\"\n [headingLevel]=\"5\"\n />\n </rb-panel>\n </div>\n\n <h3 class=\"showcase__subheading\">Status chip</h3>\n <div class=\"rb-action-group\">\n @for (chip of chipSamples; track chip.tone) {\n <rb-status-chip [tone]=\"chip.tone\" [label]=\"chip.label\" />\n }\n </div>\n <div class=\"rb-action-group\">\n @for (chip of chipSamples; track chip.tone) {\n <rb-status-chip [tone]=\"chip.tone\" [label]=\"chip.label\" size=\"compact\" />\n }\n <rb-status-chip label=\"Locked\" icon=\"lock\" size=\"compact\" />\n </div>\n\n <h3 class=\"showcase__subheading\">Technical value</h3>\n <dl class=\"rb-meta-grid\">\n <div class=\"rb-meta-grid__item\">\n <dt class=\"rb-meta-grid__label\">Fits</dt>\n <dd class=\"rb-meta-grid__value\"><rb-technical-value value=\"usr_01HZX9\" /></dd>\n </div>\n <div class=\"rb-meta-grid__item showcase__narrow\">\n <dt class=\"rb-meta-grid__label\">Truncated</dt>\n <dd class=\"rb-meta-grid__value\">\n <rb-technical-value\n value=\"https://auth.rblabs.dev/oauth/callback/photogrammetry-workspace\"\n />\n </dd>\n </div>\n <div class=\"rb-meta-grid__item\">\n <dt class=\"rb-meta-grid__label\">Shortened</dt>\n <dd class=\"rb-meta-grid__value\">\n <rb-technical-value\n value=\"sha256:2b4f69c9d1e0a7b35c8e6f41a9d2c7b0e3f5a8c1d4b7e0f2a5c8d1e4b7a0c3f6\"\n displayValue=\"sha256:2b4f69c9…\"\n />\n </dd>\n </div>\n </dl>\n\n <h3 class=\"showcase__subheading\">Confirmation dialog</h3>\n <div class=\"rb-action-group\">\n <button matButton=\"outlined\" type=\"button\" (click)=\"openConfirm('primary')\">\n Confirm an action\n </button>\n <button matButton=\"outlined\" color=\"warn\" type=\"button\" (click)=\"openConfirm('danger')\">\n Confirm a destructive action\n </button>\n <button matButton=\"outlined\" color=\"warn\" type=\"button\" (click)=\"openConfirm('reason')\">\n Confirm with a reason\n </button>\n </div>\n <rb-message [tone]=\"dialogResultTone()\" title=\"Last result\">{{ dialogResult() }}</rb-message>\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-classes\">\n <h2 id=\"showcase-classes\" class=\"showcase__heading\">Style classes</h2>\n\n <h3 class=\"showcase__subheading\">Forms</h3>\n <form class=\"rb-form-grid\" (submit)=\"$event.preventDefault()\">\n <div class=\"rb-field\">\n <label class=\"rb-field__label\" for=\"showcase-field-name\">Application name</label>\n <input\n id=\"showcase-field-name\"\n class=\"rb-field__control\"\n type=\"text\"\n value=\"Photogrammetry workspace\"\n aria-describedby=\"showcase-field-name-hint\"\n />\n <p id=\"showcase-field-name-hint\" class=\"rb-field__hint\">Shown on the consent screen.</p>\n </div>\n <div class=\"rb-field\">\n <label class=\"rb-field__label\" for=\"showcase-field-environment\">Environment</label>\n <select id=\"showcase-field-environment\" class=\"rb-field__control\">\n <option>Production</option>\n <option>Staging</option>\n </select>\n </div>\n <div class=\"rb-field rb-field--invalid\">\n <label class=\"rb-field__label\" for=\"showcase-field-uri\">Redirect URI</label>\n <input\n id=\"showcase-field-uri\"\n class=\"rb-field__control\"\n type=\"url\"\n value=\"http://photogrammetry\"\n aria-invalid=\"true\"\n aria-describedby=\"showcase-field-uri-error\"\n />\n <p id=\"showcase-field-uri-error\" class=\"rb-field__error\">\n Enter an https:// address, for example https://app.rblabs.dev/callback.\n </p>\n </div>\n <div class=\"rb-field rb-field--disabled\">\n <label class=\"rb-field__label\" for=\"showcase-field-client\">Client ID</label>\n <input\n id=\"showcase-field-client\"\n class=\"rb-field__control\"\n type=\"text\"\n value=\"app_01J2B7F9P4L6X3V8S1D5G0HAKM\"\n disabled\n aria-describedby=\"showcase-field-client-hint\"\n />\n <p id=\"showcase-field-client-hint\" class=\"rb-field__hint\">\n Generated at registration and cannot be changed.\n </p>\n </div>\n <div class=\"rb-field rb-form-grid__full\">\n <label class=\"rb-field__label\" for=\"showcase-field-notes\">Notes</label>\n <textarea\n id=\"showcase-field-notes\"\n class=\"rb-field__control\"\n placeholder=\"Optional context for other administrators\"\n ></textarea>\n </div>\n <mat-form-field appearance=\"outline\">\n <mat-label>Display name (mat-form-field)</mat-label>\n <input matInput value=\"Photogrammetry\" />\n <mat-hint>Material fields sit in the same grid.</mat-hint>\n </mat-form-field>\n <div class=\"rb-field rb-form-grid__full\">\n <span id=\"showcase-field-scopes\" class=\"rb-field__label\"\n >Granted scopes (mat-chip-grid)</span\n >\n @if (scopes().length > 0) {\n <mat-chip-grid aria-labelledby=\"showcase-field-scopes\">\n @for (scope of scopes(); track scope) {\n <mat-chip-row (removed)=\"removeScope(scope)\">\n {{ scope }}\n <button matChipRemove [attr.aria-label]=\"'Remove ' + scope\">\n <mat-icon aria-hidden=\"true\">close</mat-icon>\n </button>\n </mat-chip-row>\n }\n </mat-chip-grid>\n } @else {\n <p class=\"rb-field__hint\">No scopes granted.</p>\n }\n <p class=\"rb-field__hint\">\n Remove buttons are at least 24px on fine pointers and 44px on coarse pointers.\n @if (scopes().length < defaultScopes.length) {\n <button matButton type=\"button\" (click)=\"resetScopes()\">Restore scopes</button>\n }\n </p>\n </div>\n </form>\n\n <h3 class=\"showcase__subheading\">Tables</h3>\n <p class=\"showcase__lead\">\n Below the <code>md</code> breakpoint each row becomes a stacked record labelled from\n <code>data-label</code>.\n </p>\n <div class=\"rb-table-scroll\">\n <table class=\"rb-table\">\n <caption class=\"rb-visually-hidden\">\n Registered applications\n </caption>\n <thead>\n <tr>\n <th scope=\"col\">Name</th>\n <th scope=\"col\">Client ID</th>\n <th scope=\"col\">Status</th>\n <th scope=\"col\">Updated</th>\n <th scope=\"col\"><span class=\"rb-visually-hidden\">Actions</span></th>\n </tr>\n </thead>\n <tbody>\n @for (row of tableRows; track row.clientId) {\n <tr>\n <td data-label=\"Name\">{{ row.name }}</td>\n <td data-label=\"Client ID\" class=\"showcase__cell-id\">\n <rb-technical-value [value]=\"row.clientId\" />\n </td>\n <td data-label=\"Status\">\n <rb-status-chip\n [tone]=\"row.status.tone\"\n [label]=\"row.status.label\"\n size=\"compact\"\n />\n </td>\n <td data-label=\"Updated\">{{ row.updated }}</td>\n <td>\n <div class=\"rb-table__actions\">\n <button\n matIconButton\n type=\"button\"\n [attr.aria-label]=\"'Edit ' + row.name\"\n [matTooltip]=\"'Edit ' + row.name\"\n >\n <mat-icon aria-hidden=\"true\">edit</mat-icon>\n </button>\n <button\n matIconButton\n color=\"warn\"\n type=\"button\"\n [attr.aria-label]=\"'Delete ' + row.name\"\n [matTooltip]=\"'Delete ' + row.name\"\n >\n <mat-icon aria-hidden=\"true\">delete</mat-icon>\n </button>\n </div>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <div class=\"showcase__grid\">\n <div class=\"rb-table-scroll\">\n <table class=\"rb-table\" aria-busy=\"true\">\n <caption class=\"rb-visually-hidden\">\n Applications, loading\n </caption>\n <thead>\n <tr>\n <th scope=\"col\">Name</th>\n <th scope=\"col\">Status</th>\n </tr>\n </thead>\n <tbody>\n @for (row of [1, 2, 3]; track row) {\n <tr class=\"rb-table__skeleton-row\" aria-hidden=\"true\">\n <td><span class=\"rb-skeleton\"></span></td>\n <td><span class=\"rb-skeleton\"></span></td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n <div class=\"rb-table-scroll\">\n <table class=\"rb-table\">\n <caption class=\"rb-visually-hidden\">\n Applications matching “rover”\n </caption>\n <thead>\n <tr>\n <th scope=\"col\">Name</th>\n <th scope=\"col\">Status</th>\n </tr>\n </thead>\n <tbody>\n <tr class=\"rb-table__empty-row\">\n <td colspan=\"2\">No applications match “rover”.</td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n\n <p class=\"showcase__lead\">\n <code>mat-table</code> with <code>rb-table--inline</code>: the grid is kept on phones and\n scrolls inside <code>rb-table-scroll</code>, which is focusable so keyboard users can scroll\n it.\n </p>\n <div\n class=\"rb-table-scroll\"\n role=\"region\"\n aria-label=\"Registered applications, scrollable\"\n tabindex=\"0\"\n >\n <table mat-table class=\"rb-table rb-table--inline\" [dataSource]=\"tableRows\">\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef scope=\"col\">Name</th>\n <td mat-cell *matCellDef=\"let row\" data-label=\"Name\">{{ row.name }}</td>\n </ng-container>\n <ng-container matColumnDef=\"clientId\">\n <th mat-header-cell *matHeaderCellDef scope=\"col\">Client ID</th>\n <td mat-cell *matCellDef=\"let row\" data-label=\"Client ID\">\n <rb-technical-value [value]=\"row.clientId\" />\n </td>\n </ng-container>\n <ng-container matColumnDef=\"status\">\n <th mat-header-cell *matHeaderCellDef scope=\"col\">Status</th>\n <td mat-cell *matCellDef=\"let row\" data-label=\"Status\">\n <rb-status-chip [tone]=\"row.status.tone\" [label]=\"row.status.label\" size=\"compact\" />\n </td>\n </ng-container>\n <ng-container matColumnDef=\"updated\">\n <th mat-header-cell *matHeaderCellDef scope=\"col\">Updated</th>\n <td mat-cell *matCellDef=\"let row\" data-label=\"Updated\">{{ row.updated }}</td>\n </ng-container>\n <tr mat-header-row *matHeaderRowDef=\"tableColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: tableColumns\"></tr>\n <tr *matNoDataRow>\n <td [attr.colspan]=\"tableColumns.length\">No applications.</td>\n </tr>\n </table>\n </div>\n\n <h3 class=\"showcase__subheading\">Layout</h3>\n <div class=\"rb-master-detail\">\n <rb-panel\n title=\"rb-master-detail\"\n description=\"List on the left, detail on the right.\"\n variant=\"soft\"\n [headingLevel]=\"4\"\n >\n <div class=\"rb-stack rb-stack--sm\">\n <span class=\"rb-skeleton\" aria-hidden=\"true\"></span>\n <span class=\"rb-skeleton\" aria-hidden=\"true\"></span>\n <span class=\"rb-skeleton\" aria-hidden=\"true\"></span>\n </div>\n </rb-panel>\n <rb-panel title=\"Detail\" description=\"Stacks below the xl breakpoint.\" [headingLevel]=\"4\">\n <div class=\"rb-stack\">\n <dl class=\"rb-meta-grid\">\n <div class=\"rb-meta-grid__item\">\n <dt class=\"rb-meta-grid__label\">Owner</dt>\n <dd class=\"rb-meta-grid__value\">Platform team</dd>\n </div>\n <div class=\"rb-meta-grid__item\">\n <dt class=\"rb-meta-grid__label\">Created</dt>\n <dd class=\"rb-meta-grid__value\">3 Mar 2026</dd>\n </div>\n <div class=\"rb-meta-grid__item\">\n <dt class=\"rb-meta-grid__label\">Scopes</dt>\n <dd class=\"rb-meta-grid__value\">openid profile</dd>\n </div>\n </dl>\n <span class=\"rb-skeleton rb-skeleton--heading\" aria-hidden=\"true\"></span>\n <span class=\"rb-skeleton rb-skeleton--control\" aria-hidden=\"true\"></span>\n <div class=\"rb-action-group rb-action-group--between rb-action-group--mobile-stack\">\n <button matButton color=\"warn\" type=\"button\">Delete</button>\n <div class=\"rb-action-group rb-action-group--end rb-action-group--mobile-stack\">\n <button matButton=\"outlined\" type=\"button\">Cancel</button>\n <button matButton=\"filled\" type=\"button\">Save changes</button>\n </div>\n </div>\n </div>\n </rb-panel>\n </div>\n\n <h3 class=\"showcase__subheading\">Text and landing</h3>\n <p class=\"showcase__lead\">\n The hero, tags and icon tiles of the public home pages, and the two uppercase label roles.\n </p>\n <div class=\"rb-hero\">\n <div class=\"rb-hero__copy\">\n <p class=\"rb-eyebrow\">\n <mat-icon aria-hidden=\"true\">deployed_code</mat-icon>\n rb-eyebrow\n </p>\n <p class=\"rb-hero__title\">One standard for every RB Labs app</p>\n <p class=\"rb-hero__lead\">\n <code>.rb-hero__title</code> uses the hero type role; <code>.rb-hero__lead</code> sits\n under it. Below the lg breakpoint the media column moves under the copy.\n </p>\n <ul class=\"rb-tag-list\" aria-label=\"Stack\">\n <li>Angular</li>\n <li>Angular Material 3</li>\n <li>WCAG 2.2 AA</li>\n </ul>\n </div>\n <rb-panel title=\"Media column\" variant=\"soft\" [headingLevel]=\"4\">\n <ul class=\"showcase__icon-tiles\">\n @for (tile of iconTiles; track tile.label) {\n <li>\n <span class=\"rb-icon-tile\" aria-hidden=\"true\">\n <mat-icon>{{ tile.icon }}</mat-icon>\n </span>\n <span>\n <span class=\"rb-overline\">{{ tile.overline }}</span>\n <a class=\"rb-table__link\" routerLink=\".\" fragment=\"showcase-classes\">{{\n tile.label\n }}</a>\n </span>\n </li>\n }\n </ul>\n </rb-panel>\n </div>\n <div class=\"showcase__stage\">\n <p class=\"showcase__stage-caption\">\n Over a media stage, labels take <code>.rb-overline--on-stage</code>.\n </p>\n <dl class=\"showcase__stage-list\">\n @for (metric of stageMetrics; track metric.label) {\n <div>\n <dt class=\"rb-overline rb-overline--on-stage\">{{ metric.label }}</dt>\n <dd>{{ metric.value }}</dd>\n </div>\n }\n </dl>\n </div>\n </section>\n </main>\n}\n",
1036
+ styles: [":host{display:block;min-height:100dvh;padding:var(--rb-space-page)}:host(.showcase--example){padding:0}.showcase__main{width:min(100%,var(--rb-size-content-max));margin-inline:auto}.showcase__header{padding-block-end:var(--rb-space-6);border-block-end:1px solid var(--rb-color-border)}.showcase__lead,.showcase__text{margin:0;color:var(--rb-color-text-muted);font:var(--rb-type-body-medium)}.showcase__lead{max-width:48rem}.showcase__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,18rem),1fr));gap:var(--rb-space-4);align-items:start}.showcase__narrow{max-inline-size:12rem}.showcase__cell-id{max-inline-size:14rem}.showcase__note{margin:0;color:var(--rb-color-text-muted);font:var(--rb-type-body-small)}.showcase__main{display:grid;gap:var(--rb-space-10);padding-block-end:var(--rb-space-8)}.showcase__main:focus{outline:none}.showcase__section{display:grid;gap:var(--rb-space-4);min-width:0}.showcase__heading{margin:0;color:var(--rb-color-text-strong);font:var(--rb-type-title-large)}.showcase__subheading{margin:var(--rb-space-2) 0 0;color:var(--rb-color-text-muted);font:var(--rb-type-title-small)}.showcase__swatches,.showcase__tiles,.showcase__list{margin:0;padding:0;list-style:none}.showcase__swatches{display:grid;grid-template-columns:repeat(auto-fill,minmax(12rem,1fr));gap:var(--rb-space-3)}.showcase__swatch{display:grid;gap:var(--rb-space-1);min-width:0}.showcase__swatch code{overflow-wrap:anywhere;color:var(--rb-color-text);font-size:var(--rb-type-body-small-size)}.showcase__chip{display:grid;height:3.5rem;place-items:center;border:1px solid var(--rb-color-border);border-radius:var(--rb-radius-md);font:var(--rb-type-title-medium)}.showcase__list{display:grid;gap:var(--rb-space-3)}.showcase__type-row,.showcase__space-row{display:grid;grid-template-columns:9rem minmax(0,1fr);gap:var(--rb-space-4);align-items:center}.showcase__type-row code,.showcase__space-row code{color:var(--rb-color-text-subtle);font-size:var(--rb-type-body-small-size)}@media(width<40rem){.showcase__type-row,.showcase__space-row{grid-template-columns:1fr;gap:var(--rb-space-1)}}.showcase__type-sample{overflow-wrap:anywhere;color:var(--rb-color-text-strong)}.showcase__mono{margin:0;color:var(--rb-color-text);font-family:var(--rb-font-mono)}.showcase__space-bar{display:block;height:var(--rb-space-3);border-radius:var(--rb-radius-xs);background-color:var(--rb-color-primary)}.showcase__tiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(8rem,1fr));gap:var(--rb-space-4)}.showcase__tile{display:grid;min-height:5rem;place-items:center;border:1px solid var(--rb-color-border-strong);border-radius:var(--rb-radius-lg);background-color:var(--rb-color-surface);color:var(--rb-color-text-muted);font-size:var(--rb-type-body-small-size)}.showcase__tile--raised{border-color:transparent;background-color:var(--rb-color-surface-raised)}.showcase__brand-row{display:flex;flex-wrap:wrap;gap:var(--rb-space-6);align-items:center}.showcase__brand-row--list{margin:0;padding:0;list-style:none}.showcase__monogram{display:inline-flex;gap:var(--rb-space-2);align-items:center}.showcase__monogram code{color:var(--rb-color-text-subtle);font-size:var(--rb-type-body-small-size)}.showcase__frame{display:block;inline-size:100%;block-size:40rem;border:1px solid var(--rb-color-border);border-radius:var(--rb-radius-lg);background-color:var(--rb-color-bg)}.showcase__icon-tiles{display:grid;gap:var(--rb-space-4);margin:0;padding:0;list-style:none}.showcase__icon-tiles>li{display:flex;gap:var(--rb-space-3);align-items:center}.showcase__icon-tiles>li>span:last-child{display:grid;gap:var(--rb-space-1)}.showcase__stage{display:grid;gap:var(--rb-space-4);margin-block-start:var(--rb-space-6);padding:var(--rb-space-6);border-radius:var(--rb-radius-lg);background-color:var(--rb-color-stage);color:var(--rb-color-on-stage)}.showcase__stage-caption{margin:0;font:var(--rb-type-body-medium)}.showcase__stage-caption code{font-size:var(--rb-type-body-small-size)}.showcase__stage-list{display:flex;flex-wrap:wrap;gap:var(--rb-space-2) var(--rb-space-8);margin:0}.showcase__stage-list dd{margin:0;font:var(--rb-type-body-medium);font-variant-numeric:tabular-nums}\n"]
1022
1037
  }]
1023
1038
  }]
1024
1039
  });
@@ -1 +1 @@
1
- {"version":3,"file":"rblabs-dev-axiom-showcase.mjs","names":[],"sources":["../../../projects/axiom/showcase/src/shell-example/shell-example.ts","../../../projects/axiom/showcase/src/shell-example/shell-example.html","../../../projects/axiom/showcase/src/design-system-showcase.ts","../../../projects/axiom/showcase/src/design-system-showcase.html"],"sourcesContent":["import { NgTemplateOutlet } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { MatButton, MatIconButton } from '@angular/material/button';\nimport { MatIcon } from '@angular/material/icon';\nimport { MatTooltip } from '@angular/material/tooltip';\nimport { ActivatedRoute, RouterLink } from '@angular/router';\nimport { map } from 'rxjs';\n\nimport {\n RbAppShellComponent,\n RbCentralSignInComponent,\n RbPageHeaderComponent,\n RbPanelComponent,\n RbPublicShellComponent,\n} from '@rblabs-dev/axiom';\n\nexport type RbShellExampleKind = 'app-sidenav' | 'app-modules' | 'public' | 'sign-in';\n\nexport const RB_SHELL_EXAMPLE_KINDS: readonly RbShellExampleKind[] = [\n 'app-sidenav',\n 'app-modules',\n 'public',\n 'sign-in',\n];\n\ninterface ExampleLink {\n id: string;\n label: string;\n icon: string;\n}\n\nconst SIDENAV_SECTIONS: { label: string; links: ExampleLink[] }[] = [\n { label: 'Workspace', links: [{ id: 'overview', label: 'Overview', icon: 'space_dashboard' }] },\n {\n label: 'Administration',\n links: [\n { id: 'users', label: 'Users', icon: 'group' },\n { id: 'applications', label: 'Applications', icon: 'apps' },\n { id: 'services', label: 'Services', icon: 'dns' },\n { id: 'audit', label: 'Registry audit', icon: 'history' },\n ],\n },\n];\n\nconst MODULE_LINKS: ExampleLink[] = [\n { id: 'launch', label: 'Launch', icon: 'add_box' },\n { id: 'datasets', label: 'Datasets', icon: 'inbox' },\n { id: 'jobs', label: 'Jobs', icon: 'autorenew' },\n { id: 'artifacts', label: 'Artifacts', icon: 'deployed_code' },\n];\n\n/**\n * Full-page shell examples for the showcase (`/design-system?example=<kind>`), so each shell is\n * shown in a document of its own: with its own landmarks, at the real viewport width.\n */\n@Component({\n selector: 'rb-showcase-shell-example',\n imports: [\n MatButton,\n MatIcon,\n MatIconButton,\n MatTooltip,\n NgTemplateOutlet,\n RbAppShellComponent,\n RbCentralSignInComponent,\n RbPageHeaderComponent,\n RbPanelComponent,\n RbPublicShellComponent,\n RouterLink,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './shell-example.html',\n styleUrl: './shell-example.scss',\n})\nexport class RbShellExampleComponent {\n readonly kind = input.required<RbShellExampleKind>();\n\n protected readonly sidenavSections = SIDENAV_SECTIONS;\n protected readonly moduleLinks = MODULE_LINKS;\n\n private readonly section = toSignal(\n inject(ActivatedRoute).queryParamMap.pipe(map((params) => params.get('section'))),\n { initialValue: null },\n );\n\n /** The current link: the `section` query parameter, or the first link of the example. */\n protected readonly current = computed(() => {\n const links =\n this.kind() === 'app-modules'\n ? MODULE_LINKS\n : SIDENAV_SECTIONS.flatMap((section) => section.links);\n return links.find((link) => link.id === this.section()) ?? links[0];\n });\n}\n","@switch (kind()) {\n @case ('app-sidenav') {\n <rb-app-shell appName=\"Admin\" homeLink=\"/design-system\" navigation=\"sidenav\">\n <span rbShellTitle>Administration workspace</span>\n <button\n rbShellActions\n matIconButton\n type=\"button\"\n aria-label=\"Sign out\"\n matTooltip=\"Sign out\"\n >\n <mat-icon aria-hidden=\"true\">logout</mat-icon>\n </button>\n\n <nav rbShellSidenav aria-label=\"Administration\">\n @for (section of sidenavSections; track section.label) {\n <h2 class=\"rb-shell-nav-heading\">{{ section.label }}</h2>\n @for (link of section.links; track link.id) {\n <a\n class=\"rb-shell-nav-link\"\n routerLink=\".\"\n [queryParams]=\"{ example: kind(), section: link.id }\"\n [attr.aria-current]=\"current().id === link.id ? 'page' : null\"\n >\n <mat-icon aria-hidden=\"true\">{{ link.icon }}</mat-icon>\n <span>{{ link.label }}</span>\n </a>\n }\n }\n </nav>\n\n <ng-container [ngTemplateOutlet]=\"page\" />\n </rb-app-shell>\n }\n @case ('app-modules') {\n <rb-app-shell appName=\"Photogrammetry\" homeLink=\"/design-system\" navigation=\"modules\">\n <span rbShellTitle>Workspace · {{ current().label }}</span>\n <button\n rbShellActions\n matIconButton\n type=\"button\"\n aria-label=\"Sign out\"\n matTooltip=\"Sign out\"\n >\n <mat-icon aria-hidden=\"true\">logout</mat-icon>\n </button>\n\n <nav rbShellModuleNav aria-label=\"Workspace modules\">\n @for (link of moduleLinks; track link.id) {\n <a\n class=\"rb-shell-nav-link\"\n routerLink=\".\"\n [queryParams]=\"{ example: kind(), section: link.id }\"\n [attr.aria-current]=\"current().id === link.id ? 'page' : null\"\n >\n <mat-icon aria-hidden=\"true\">{{ link.icon }}</mat-icon>\n <span>{{ link.label }}</span>\n </a>\n }\n </nav>\n\n <span rbShellFooter>\n <mat-icon class=\"shell-example__footer-icon\" aria-hidden=\"true\">lock</mat-icon>\n Protected access\n </span>\n\n <ng-container [ngTemplateOutlet]=\"page\" />\n </rb-app-shell>\n }\n @case ('public') {\n <rb-public-shell appName=\"Auth\" homeLink=\"/design-system\">\n <a rbShellFooter routerLink=\"/design-system\">Design system</a>\n\n <section class=\"shell-example__card\" aria-labelledby=\"shell-example-sign-in\">\n <h1 id=\"shell-example-sign-in\" class=\"shell-example__card-title\">Sign in to RB Labs</h1>\n <p class=\"shell-example__card-text\">One account for Admin, Rover and Photogrammetry.</p>\n <form class=\"rb-stack\" (submit)=\"$event.preventDefault()\">\n <div class=\"rb-field\">\n <label class=\"rb-field__label\" for=\"shell-example-email\">Email</label>\n <input\n id=\"shell-example-email\"\n class=\"rb-field__control\"\n type=\"email\"\n autocomplete=\"email\"\n />\n </div>\n <div class=\"rb-field\">\n <label class=\"rb-field__label\" for=\"shell-example-password\">Password</label>\n <input\n id=\"shell-example-password\"\n class=\"rb-field__control\"\n type=\"password\"\n autocomplete=\"current-password\"\n />\n </div>\n <button matButton=\"filled\" type=\"submit\">Sign in</button>\n </form>\n </section>\n </rb-public-shell>\n }\n @case ('sign-in') {\n <rb-public-shell appName=\"Admin\" homeLink=\"/design-system\">\n <a rbShellFooter routerLink=\"/design-system\">Design system</a>\n\n <rb-central-sign-in\n appName=\"Admin\"\n eyebrow=\"Administration workspace\"\n note=\"Restricted to active RB Labs administrators.\"\n backLink=\"/design-system\"\n />\n </rb-public-shell>\n }\n}\n\n<ng-template #page>\n <div class=\"rb-stack\">\n <rb-page-header\n eyebrow=\"Shell example\"\n [title]=\"current().label\"\n description=\"Placeholder content inside the shell's content column.\"\n />\n <div class=\"shell-example__grid\">\n <rb-panel title=\"Content column\" [headingLevel]=\"2\">\n <p class=\"shell-example__text\">\n The column is centered and stops at <code>--rb-size-content-max</code>. Its gutter is\n <code>--rb-space-page</code>.\n </p>\n </rb-panel>\n <rb-panel title=\"Navigation\" variant=\"soft\" [headingLevel]=\"2\">\n <p class=\"shell-example__text\">\n Choose another item: the current one carries <code>aria-current=\"page\"</code>, and below\n <code>lg</code> the drawer closes on navigation.\n </p>\n </rb-panel>\n </div>\n <rb-panel title=\"Recent activity\" [headingLevel]=\"2\">\n <button rbActions matButton type=\"button\">\n <mat-icon aria-hidden=\"true\">refresh</mat-icon>\n Refresh\n </button>\n <div class=\"rb-stack rb-stack--sm\" aria-hidden=\"true\">\n @for (row of [1, 2, 3, 4, 5, 6]; track row) {\n <span class=\"rb-skeleton\"></span>\n }\n </div>\n </rb-panel>\n </div>\n</ng-template>\n","import { ChangeDetectionStrategy, Component, computed, inject, signal } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { MatButton, MatIconButton } from '@angular/material/button';\nimport { MatChipGrid, MatChipRemove, MatChipRow } from '@angular/material/chips';\nimport { MatFormField, MatHint, MatLabel } from '@angular/material/form-field';\nimport { MatIcon } from '@angular/material/icon';\nimport { MatInput } from '@angular/material/input';\nimport { MatTableModule } from '@angular/material/table';\nimport { MatTooltip } from '@angular/material/tooltip';\nimport { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';\nimport { ActivatedRoute, RouterLink } from '@angular/router';\nimport { map } from 'rxjs';\n\nimport {\n RB_DESIGN_SYSTEM_VERSION,\n RbBrandLockupComponent,\n RbConfirmDialogData,\n RbConfirmDialogResult,\n RbConfirmDialogService,\n RbEmptyStateComponent,\n RbMessageComponent,\n RbMessageTone,\n RbMonogramComponent,\n RbPageHeaderComponent,\n RbPageHeaderMetric,\n RbPanelComponent,\n RbStatusChipComponent,\n RbStatusTone,\n RbTechnicalValueComponent,\n RbThemeToggleComponent,\n ThemeService,\n} from '@rblabs-dev/axiom';\n\nimport {\n RB_SHELL_EXAMPLE_KINDS,\n RbShellExampleComponent,\n RbShellExampleKind,\n} from './shell-example/shell-example';\n\ninterface ColorGroup {\n title: string;\n roles: { token: string; on?: string }[];\n}\n\nconst COLOR_GROUPS: ColorGroup[] = [\n {\n title: 'Primary and tertiary',\n roles: [\n { token: 'primary', on: 'on-primary' },\n { token: 'primary-hover', on: 'on-primary' },\n { token: 'primary-soft', on: 'on-primary-soft' },\n { token: 'tertiary', on: 'on-tertiary' },\n ],\n },\n {\n title: 'Surfaces',\n roles: [\n { token: 'bg', on: 'text' },\n { token: 'surface-sunken', on: 'text' },\n { token: 'surface', on: 'text' },\n { token: 'surface-raised', on: 'text' },\n { token: 'surface-hover', on: 'text' },\n { token: 'surface-nav', on: 'text' },\n { token: 'control', on: 'text-strong' },\n ],\n },\n {\n title: 'Text and borders',\n roles: [\n { token: 'text-strong' },\n { token: 'text' },\n { token: 'text-muted' },\n { token: 'text-subtle' },\n { token: 'text-disabled' },\n { token: 'border' },\n { token: 'border-strong' },\n { token: 'outline' },\n { token: 'focus' },\n ],\n },\n {\n title: 'Status',\n roles: [\n { token: 'info', on: 'on-info' },\n { token: 'info-soft', on: 'info' },\n { token: 'success', on: 'on-success' },\n { token: 'success-soft', on: 'success' },\n { token: 'warning', on: 'on-warning' },\n { token: 'warning-soft', on: 'warning' },\n { token: 'danger', on: 'on-danger' },\n { token: 'danger-soft', on: 'danger' },\n ],\n },\n];\n\nconst TYPE_ROLES = [\n 'hero',\n 'page-title',\n 'display-small',\n 'headline-large',\n 'headline-medium',\n 'headline-small',\n 'title-large',\n 'title-medium',\n 'title-small',\n 'body-large',\n 'body-medium',\n 'body-small',\n 'label-large',\n 'label-medium',\n 'label-small',\n];\n\nconst MESSAGE_SAMPLES: { tone: RbMessageTone; title: string; body: string }[] = [\n { tone: 'neutral', title: 'Read-only', body: 'Registry entries are managed by the deploy job.' },\n { tone: 'info', title: 'Sync scheduled', body: 'The registry syncs again in 5 minutes.' },\n { tone: 'success', title: 'Application saved', body: 'Redirect URIs take effect immediately.' },\n { tone: 'warning', title: 'Key expires soon', body: 'Rotate it before 1 November 2026.' },\n { tone: 'danger', title: 'Sync failed', body: 'The registry could not be reached.' },\n];\n\nconst CHIP_SAMPLES: { tone: RbStatusTone; label: string }[] = [\n { tone: 'active', label: 'Running' },\n { tone: 'success', label: 'Active' },\n { tone: 'warning', label: 'Disabled' },\n { tone: 'danger', label: 'Revoked' },\n { tone: 'neutral', label: 'Draft' },\n];\n\nexport interface ShowcaseRow {\n name: string;\n clientId: string;\n status: { tone: RbStatusTone; label: string };\n updated: string;\n}\n\nconst TABLE_ROWS: ShowcaseRow[] = [\n {\n name: 'RB Labs Auth',\n clientId: 'app_01HZX9K4T7Q2M8R5W3N6B1C0DE',\n status: { tone: 'success', label: 'Active' },\n updated: '19 Oct 2026',\n },\n {\n name: 'Photogrammetry workspace',\n clientId: 'app_01J2B7F9P4L6X3V8S1D5G0HAKM',\n status: { tone: 'warning', label: 'Disabled' },\n updated: '2 Oct 2026',\n },\n {\n name: 'Rover control',\n clientId: 'app_01J3C1A8E5N2Y7U4Q9T6R0ZWXP',\n status: { tone: 'danger', label: 'Revoked' },\n updated: '14 Sep 2026',\n },\n];\n\nconst SHELL_EXAMPLES: { kind: RbShellExampleKind; title: string; description: string }[] = [\n {\n kind: 'app-sidenav',\n title: 'App shell with sidenav',\n description: 'Admin-style navigation rail; an overlay drawer below lg.',\n },\n {\n kind: 'app-modules',\n title: 'App shell with module nav',\n description: 'Photogrammetry-style modules; a row below lg and a tab bar below sm.',\n },\n {\n kind: 'public',\n title: 'Public shell',\n description: 'Landing, sign-in and other public pages.',\n },\n {\n kind: 'sign-in',\n title: 'Central sign-in',\n description: 'The page that hands off to RB Labs Auth, alone in the public shell.',\n },\n];\n\nconst DEFAULT_SCOPES = ['openid', 'profile', 'email', 'offline_access'];\n\nconst ICON_TILES = [\n { icon: 'group', overline: 'rb-icon-tile', label: 'Users and roles' },\n { icon: 'apps', overline: 'rb-overline', label: 'Registered applications' },\n { icon: 'history', overline: 'rb-table__link', label: 'Registry audit' },\n];\n\nconst MONOGRAM_SIZES = [\n { token: '--rb-size-monogram', value: 'var(--rb-size-monogram)' },\n { token: '--rb-size-monogram-sm', value: 'var(--rb-size-monogram-sm)' },\n { token: 'favicon (16px)', value: 'var(--rb-size-icon-sm)' },\n];\n\nconst BUTTON_ROLES = [\n { role: 'Primary', markup: 'matButton=\"filled\"', legacy: 'mat-flat-button' },\n { role: 'Secondary', markup: 'matButton=\"outlined\"', legacy: 'mat-stroked-button' },\n { role: 'Tertiary', markup: 'matButton', legacy: 'mat-button' },\n {\n role: 'Danger',\n markup: 'matButton=\"filled\" color=\"warn\"',\n legacy: 'mat-flat-button color=\"warn\"',\n },\n { role: 'Icon', markup: 'matIconButton', legacy: 'mat-icon-button' },\n];\n\n/**\n * Living reference of the kit (docs/design-system.md): tokens, type, spacing, shape, elevation,\n * Material components, the content components and the kit style classes, in the active theme.\n */\n@Component({\n selector: 'rb-design-system-showcase',\n imports: [\n MatButton,\n MatChipGrid,\n MatChipRemove,\n MatChipRow,\n MatFormField,\n MatHint,\n MatIcon,\n MatIconButton,\n MatInput,\n MatLabel,\n MatTableModule,\n MatTooltip,\n RbBrandLockupComponent,\n RbEmptyStateComponent,\n RbMessageComponent,\n RbMonogramComponent,\n RbPageHeaderComponent,\n RbPanelComponent,\n RbStatusChipComponent,\n RbShellExampleComponent,\n RbTechnicalValueComponent,\n RbThemeToggleComponent,\n RouterLink,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './design-system-showcase.html',\n styleUrl: './design-system-showcase.scss',\n // `rb-scope` activates the kit in apps that still include it scoped.\n host: { class: 'rb-scope', '[class.showcase--example]': 'example() !== null' },\n})\nexport class DesignSystemShowcaseComponent {\n protected readonly theme = inject(ThemeService);\n private readonly confirmDialog = inject(RbConfirmDialogService);\n\n protected readonly version = RB_DESIGN_SYSTEM_VERSION;\n protected readonly colorGroups = COLOR_GROUPS;\n protected readonly typeRoles = TYPE_ROLES;\n protected readonly spaceSteps = ['1', '2', '3', '4', '5', '6', '8', '10', '12', '16'];\n protected readonly radii = ['xs', 'sm', 'md', 'lg', 'xl', 'full'];\n protected readonly elevations = ['0', '1', '2', '3'];\n protected readonly messageSamples = MESSAGE_SAMPLES;\n protected readonly chipSamples = CHIP_SAMPLES;\n protected readonly tableRows = TABLE_ROWS;\n protected readonly tableColumns = ['name', 'clientId', 'status', 'updated'];\n protected readonly buttonRoles = BUTTON_ROLES;\n protected readonly monogramSizes = MONOGRAM_SIZES;\n protected readonly iconTiles = ICON_TILES;\n\n /** `?example=<kind>` renders one shell example full page instead of the reference. */\n protected readonly example = toSignal(\n inject(ActivatedRoute).queryParamMap.pipe(\n map((params) => {\n const kind = params.get('example') as RbShellExampleKind | null;\n return kind && RB_SHELL_EXAMPLE_KINDS.includes(kind) ? kind : null;\n }),\n ),\n { initialValue: null },\n );\n\n // Relative to the base href: the showcase is routed at `design-system` in every app. The frames\n // are re-created when the theme changes (see the template), so they pick up a stored choice.\n private readonly sanitizer = inject(DomSanitizer);\n protected readonly shellExamples = SHELL_EXAMPLES.map((example) => ({\n ...example,\n src: this.sanitizer.bypassSecurityTrustResourceUrl(\n `design-system?example=${example.kind}`,\n ) as SafeResourceUrl,\n }));\n\n protected readonly headerMetrics = computed<RbPageHeaderMetric[]>(() => [\n { label: 'Kit version', value: this.version },\n { label: 'Theme', value: this.theme.isDark() ? 'Dark' : 'Light' },\n { label: 'Components', value: 12 },\n { label: 'Contrast', value: 'WCAG 2.2 AA', tone: 'success' },\n ]);\n\n protected readonly defaultScopes = DEFAULT_SCOPES;\n protected readonly scopes = signal(DEFAULT_SCOPES);\n\n /** Mounted from the start so the result of each dialog is announced (section 9.5). */\n protected readonly dialogResult = signal('No confirmation opened yet.');\n protected readonly dialogResultTone = signal<RbMessageTone>('neutral');\n\n protected async openConfirm(kind: 'primary' | 'danger' | 'reason'): Promise<void> {\n const data: RbConfirmDialogData =\n kind === 'primary'\n ? {\n title: 'Publish release notes?',\n description: 'Every signed-in user sees the notes on their next visit.',\n confirmLabel: 'Publish notes',\n }\n : {\n title: 'Revoke this credential?',\n description: 'Clients using it stop working immediately. This cannot be undone.',\n confirmLabel: 'Revoke credential',\n tone: 'danger',\n ...(kind === 'reason'\n ? { reason: { label: 'Reason for revoking', hint: 'Recorded in the audit log.' } }\n : {}),\n };\n this.showResult(await this.confirmDialog.confirm(data));\n }\n\n protected removeScope(scope: string): void {\n this.scopes.update((scopes) => scopes.filter((item) => item !== scope));\n }\n\n protected resetScopes(): void {\n this.scopes.set(DEFAULT_SCOPES);\n }\n\n private showResult(result: RbConfirmDialogResult): void {\n this.dialogResultTone.set(result.confirmed ? 'success' : 'neutral');\n this.dialogResult.set(\n result.confirmed\n ? `Confirmed${result.reason ? ` with reason “${result.reason}”` : ''}.`\n : 'Cancelled. Nothing changed.',\n );\n }\n}\n","@if (example(); as kind) {\n <rb-showcase-shell-example [kind]=\"kind\" />\n} @else {\n <!-- Focuses main itself: under <base href=\"/\"> the bare fragment would load the root route. -->\n <a\n class=\"rb-skip-link\"\n href=\"#rb-showcase-main\"\n (click)=\"$event.preventDefault(); showcaseMain.focus()\"\n >Skip to main content</a\n >\n\n <main\n #showcaseMain\n id=\"rb-showcase-main\"\n class=\"showcase__main\"\n tabindex=\"-1\"\n aria-label=\"Design system reference\"\n >\n <rb-page-header\n class=\"showcase__header\"\n eyebrow=\"RB Labs Design System\"\n title=\"Foundations and components\"\n [description]=\"\n theme.preference() === null\n ? 'Tokens, Material components and the content kit, following the operating system theme.'\n : 'Tokens, Material components and the content kit.'\n \"\n [metrics]=\"headerMetrics()\"\n >\n <rb-theme-toggle rbActions />\n </rb-page-header>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-color\">\n <h2 id=\"showcase-color\" class=\"showcase__heading\">Color roles</h2>\n @for (group of colorGroups; track group.title) {\n <h3 class=\"showcase__subheading\">{{ group.title }}</h3>\n <ul class=\"showcase__swatches\">\n @for (role of group.roles; track role.token) {\n <li class=\"showcase__swatch\">\n <span\n class=\"showcase__chip\"\n [style.background-color]=\"'var(--rb-color-' + role.token + ')'\"\n [style.color]=\"role.on ? 'var(--rb-color-' + role.on + ')' : null\"\n aria-hidden=\"true\"\n >{{ role.on ? 'Aa' : '' }}</span\n >\n <code>--rb-color-{{ role.token }}</code>\n @if (role.on) {\n <span class=\"showcase__note\">text: {{ role.on }}</span>\n }\n </li>\n }\n </ul>\n }\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-type\">\n <h2 id=\"showcase-type\" class=\"showcase__heading\">Type scale</h2>\n <ul class=\"showcase__list\">\n @for (role of typeRoles; track role) {\n <li class=\"showcase__type-row\">\n <code>{{ role }}</code>\n <span\n class=\"showcase__type-sample\"\n [style.font]=\"'var(--rb-type-' + role + ')'\"\n [style.letter-spacing]=\"'var(--rb-type-' + role + '-tracking)'\"\n >Operate RB Labs</span\n >\n </li>\n }\n </ul>\n <p class=\"showcase__mono\">ui-monospace · usr_01HZX9 · sha256:2b4f69c9</p>\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-space\">\n <h2 id=\"showcase-space\" class=\"showcase__heading\">Spacing</h2>\n <ul class=\"showcase__list\">\n @for (step of spaceSteps; track step) {\n <li class=\"showcase__space-row\">\n <code>--rb-space-{{ step }}</code>\n <span\n class=\"showcase__space-bar\"\n [style.inline-size]=\"'var(--rb-space-' + step + ')'\"\n aria-hidden=\"true\"\n ></span>\n </li>\n }\n </ul>\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-shape\">\n <h2 id=\"showcase-shape\" class=\"showcase__heading\">Radius and elevation</h2>\n <ul class=\"showcase__tiles\">\n @for (radius of radii; track radius) {\n <li class=\"showcase__tile\" [style.border-radius]=\"'var(--rb-radius-' + radius + ')'\">\n <code>radius-{{ radius }}</code>\n </li>\n }\n </ul>\n <ul class=\"showcase__tiles\">\n @for (level of elevations; track level) {\n <li\n class=\"showcase__tile showcase__tile--raised\"\n [style.box-shadow]=\"'var(--rb-elevation-' + level + ')'\"\n >\n <code>elevation-{{ level }}</code>\n </li>\n }\n </ul>\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-material\">\n <h2 id=\"showcase-material\" class=\"showcase__heading\">Buttons</h2>\n <p class=\"showcase__lead\">\n There is no kit button component. Each role maps to an Angular Material button variant.\n </p>\n <div class=\"rb-table-scroll\">\n <table class=\"rb-table\">\n <caption class=\"rb-visually-hidden\">\n Button roles and their Material markup\n </caption>\n <thead>\n <tr>\n <th scope=\"col\">Role</th>\n <th scope=\"col\">Markup</th>\n <th scope=\"col\">Equivalent selector</th>\n </tr>\n </thead>\n <tbody>\n @for (button of buttonRoles; track button.role) {\n <tr>\n <td data-label=\"Role\">{{ button.role }}</td>\n <td data-label=\"Markup\">\n <code>{{ button.markup }}</code>\n </td>\n <td data-label=\"Equivalent selector\">\n <code>{{ button.legacy }}</code>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n <div class=\"rb-action-group\">\n <button matButton=\"filled\" type=\"button\">Primary</button>\n <button matButton=\"outlined\" type=\"button\">Secondary</button>\n <button matButton type=\"button\">Tertiary</button>\n <button matButton=\"tonal\" type=\"button\">Tonal</button>\n <button matButton=\"filled\" type=\"button\" disabled>Disabled</button>\n </div>\n <div class=\"rb-action-group\">\n <button matButton=\"filled\" color=\"warn\" type=\"button\">\n <mat-icon aria-hidden=\"true\">delete</mat-icon>\n Delete\n </button>\n <button matButton=\"outlined\" color=\"warn\" type=\"button\">Revoke</button>\n <button matButton color=\"warn\" type=\"button\">Remove</button>\n <button\n matIconButton\n color=\"warn\"\n type=\"button\"\n aria-label=\"Delete item\"\n matTooltip=\"Delete item\"\n >\n <mat-icon aria-hidden=\"true\">delete</mat-icon>\n </button>\n <button matIconButton type=\"button\" aria-label=\"Edit item\" matTooltip=\"Edit item\">\n <mat-icon aria-hidden=\"true\">edit</mat-icon>\n </button>\n </div>\n <p class=\"showcase__lead\">\n A link that navigates uses the same variants on an <code>&lt;a&gt;</code>. On coarse\n pointers it reaches the touch target like a button.\n </p>\n <div class=\"rb-action-group\">\n <a matButton=\"filled\" routerLink=\".\" fragment=\"showcase-material\">Primary link</a>\n <a matButton=\"outlined\" routerLink=\".\" fragment=\"showcase-material\">Secondary link</a>\n <a matButton routerLink=\".\" fragment=\"showcase-material\">Tertiary link</a>\n <a matButton=\"tonal\" routerLink=\".\" fragment=\"showcase-material\">Tonal link</a>\n <a\n matIconButton\n routerLink=\".\"\n fragment=\"showcase-material\"\n aria-label=\"Go to buttons\"\n matTooltip=\"Go to buttons\"\n >\n <mat-icon aria-hidden=\"true\">arrow_forward</mat-icon>\n </a>\n </div>\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-shells\">\n <h2 id=\"showcase-shells\" class=\"showcase__heading\">Shells and brand</h2>\n\n <h3 class=\"showcase__subheading\">Brand lockup and monogram</h3>\n <div class=\"showcase__brand-row\">\n <rb-brand-lockup appName=\"Admin\" link=\"/design-system\" />\n <rb-brand-lockup appName=\"Photogrammetry\" link=\"/design-system\" />\n <rb-brand-lockup appName=\"Rover\" link=\"/design-system\" compact />\n </div>\n <ul class=\"showcase__brand-row showcase__brand-row--list\" aria-label=\"Monogram sizes\">\n @for (size of monogramSizes; track size.token) {\n <li class=\"showcase__monogram\">\n <rb-monogram [style.inline-size]=\"size.value\" />\n <code>{{ size.token }}</code>\n </li>\n }\n </ul>\n\n <p class=\"showcase__lead\">\n Each preview is a frame whose width is the viewport, so on narrow screens the sidenav\n becomes a drawer and the module nav a tab bar. The previews are inert, so their landmarks do\n not repeat in this page; open the full page to use an example.\n </p>\n @for (example of shellExamples; track example.kind + theme.theme()) {\n <h3 class=\"showcase__subheading\">{{ example.title }}</h3>\n <p class=\"showcase__lead\">\n {{ example.description }}\n <a routerLink=\".\" [queryParams]=\"{ example: example.kind }\">Open full page</a>\n </p>\n <iframe\n class=\"showcase__frame\"\n [title]=\"example.title + ' example'\"\n [src]=\"example.src\"\n loading=\"lazy\"\n inert\n ></iframe>\n }\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-components\">\n <h2 id=\"showcase-components\" class=\"showcase__heading\">Content components</h2>\n\n <h3 class=\"showcase__subheading\">Page header</h3>\n <p class=\"showcase__lead\">\n The header of this page is an <code>rb-page-header</code> with an eyebrow, description,\n metrics and the theme toggle as its action. A page has exactly one.\n </p>\n\n <h3 class=\"showcase__subheading\">Panel</h3>\n <div class=\"showcase__grid\">\n <rb-panel title=\"Default\" description=\"Cards and grouped content.\" [headingLevel]=\"4\">\n <button rbActions matButton type=\"button\">\n <mat-icon aria-hidden=\"true\">refresh</mat-icon>\n Refresh\n </button>\n <p class=\"showcase__text\">Surface background, border and elevation 1.</p>\n </rb-panel>\n <rb-panel\n title=\"Soft\"\n description=\"Secondary or inset content.\"\n variant=\"soft\"\n [headingLevel]=\"4\"\n >\n <p class=\"showcase__text\">Sunken surface, no shadow.</p>\n </rb-panel>\n <rb-panel\n title=\"Elevated\"\n description=\"Content that floats above the page.\"\n variant=\"elevated\"\n [headingLevel]=\"4\"\n >\n <p class=\"showcase__text\">Raised surface and elevation 2.</p>\n </rb-panel>\n <rb-panel\n title=\"Danger zone\"\n description=\"Destructive settings.\"\n variant=\"danger\"\n [headingLevel]=\"4\"\n >\n <button rbActions matButton=\"outlined\" color=\"warn\" type=\"button\">Delete</button>\n <p class=\"showcase__text\">Danger border and title; the title names the risk.</p>\n </rb-panel>\n </div>\n\n <h3 class=\"showcase__subheading\">Message</h3>\n <div class=\"rb-stack rb-stack--sm\">\n @for (sample of messageSamples; track sample.tone) {\n <rb-message [tone]=\"sample.tone\" [title]=\"sample.title\" live=\"off\">\n {{ sample.body }}\n </rb-message>\n }\n </div>\n\n <h3 class=\"showcase__subheading\">Empty state</h3>\n <div class=\"showcase__grid\">\n <rb-panel title=\"Applications\" [headingLevel]=\"4\">\n <rb-empty-state\n icon=\"apps\"\n title=\"No applications yet\"\n description=\"Register a client to issue credentials for RB Labs sign-in.\"\n actionLabel=\"Register application\"\n [headingLevel]=\"5\"\n />\n </rb-panel>\n <rb-panel title=\"Audit history\" variant=\"soft\" [headingLevel]=\"4\">\n <rb-empty-state\n icon=\"history\"\n title=\"Nothing recorded\"\n description=\"Changes to this application appear here.\"\n layout=\"inline\"\n [headingLevel]=\"5\"\n />\n </rb-panel>\n </div>\n\n <h3 class=\"showcase__subheading\">Status chip</h3>\n <div class=\"rb-action-group\">\n @for (chip of chipSamples; track chip.tone) {\n <rb-status-chip [tone]=\"chip.tone\" [label]=\"chip.label\" />\n }\n </div>\n <div class=\"rb-action-group\">\n @for (chip of chipSamples; track chip.tone) {\n <rb-status-chip [tone]=\"chip.tone\" [label]=\"chip.label\" size=\"compact\" />\n }\n <rb-status-chip label=\"Locked\" icon=\"lock\" size=\"compact\" />\n </div>\n\n <h3 class=\"showcase__subheading\">Technical value</h3>\n <dl class=\"rb-meta-grid\">\n <div class=\"rb-meta-grid__item\">\n <dt class=\"rb-meta-grid__label\">Fits</dt>\n <dd class=\"rb-meta-grid__value\"><rb-technical-value value=\"usr_01HZX9\" /></dd>\n </div>\n <div class=\"rb-meta-grid__item showcase__narrow\">\n <dt class=\"rb-meta-grid__label\">Truncated</dt>\n <dd class=\"rb-meta-grid__value\">\n <rb-technical-value\n value=\"https://auth.rblabs.dev/oauth/callback/photogrammetry-workspace\"\n />\n </dd>\n </div>\n <div class=\"rb-meta-grid__item\">\n <dt class=\"rb-meta-grid__label\">Shortened</dt>\n <dd class=\"rb-meta-grid__value\">\n <rb-technical-value\n value=\"sha256:2b4f69c9d1e0a7b35c8e6f41a9d2c7b0e3f5a8c1d4b7e0f2a5c8d1e4b7a0c3f6\"\n displayValue=\"sha256:2b4f69c9…\"\n />\n </dd>\n </div>\n </dl>\n\n <h3 class=\"showcase__subheading\">Confirmation dialog</h3>\n <div class=\"rb-action-group\">\n <button matButton=\"outlined\" type=\"button\" (click)=\"openConfirm('primary')\">\n Confirm an action\n </button>\n <button matButton=\"outlined\" color=\"warn\" type=\"button\" (click)=\"openConfirm('danger')\">\n Confirm a destructive action\n </button>\n <button matButton=\"outlined\" color=\"warn\" type=\"button\" (click)=\"openConfirm('reason')\">\n Confirm with a reason\n </button>\n </div>\n <rb-message [tone]=\"dialogResultTone()\" title=\"Last result\">{{ dialogResult() }}</rb-message>\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-classes\">\n <h2 id=\"showcase-classes\" class=\"showcase__heading\">Style classes</h2>\n\n <h3 class=\"showcase__subheading\">Forms</h3>\n <form class=\"rb-form-grid\" (submit)=\"$event.preventDefault()\">\n <div class=\"rb-field\">\n <label class=\"rb-field__label\" for=\"showcase-field-name\">Application name</label>\n <input\n id=\"showcase-field-name\"\n class=\"rb-field__control\"\n type=\"text\"\n value=\"Photogrammetry workspace\"\n aria-describedby=\"showcase-field-name-hint\"\n />\n <p id=\"showcase-field-name-hint\" class=\"rb-field__hint\">Shown on the consent screen.</p>\n </div>\n <div class=\"rb-field\">\n <label class=\"rb-field__label\" for=\"showcase-field-environment\">Environment</label>\n <select id=\"showcase-field-environment\" class=\"rb-field__control\">\n <option>Production</option>\n <option>Staging</option>\n </select>\n </div>\n <div class=\"rb-field rb-field--invalid\">\n <label class=\"rb-field__label\" for=\"showcase-field-uri\">Redirect URI</label>\n <input\n id=\"showcase-field-uri\"\n class=\"rb-field__control\"\n type=\"url\"\n value=\"http://photogrammetry\"\n aria-invalid=\"true\"\n aria-describedby=\"showcase-field-uri-error\"\n />\n <p id=\"showcase-field-uri-error\" class=\"rb-field__error\">\n Enter an https:// address, for example https://app.rblabs.dev/callback.\n </p>\n </div>\n <div class=\"rb-field rb-field--disabled\">\n <label class=\"rb-field__label\" for=\"showcase-field-client\">Client ID</label>\n <input\n id=\"showcase-field-client\"\n class=\"rb-field__control\"\n type=\"text\"\n value=\"app_01J2B7F9P4L6X3V8S1D5G0HAKM\"\n disabled\n aria-describedby=\"showcase-field-client-hint\"\n />\n <p id=\"showcase-field-client-hint\" class=\"rb-field__hint\">\n Generated at registration and cannot be changed.\n </p>\n </div>\n <div class=\"rb-field rb-form-grid__full\">\n <label class=\"rb-field__label\" for=\"showcase-field-notes\">Notes</label>\n <textarea\n id=\"showcase-field-notes\"\n class=\"rb-field__control\"\n placeholder=\"Optional context for other administrators\"\n ></textarea>\n </div>\n <mat-form-field appearance=\"outline\">\n <mat-label>Display name (mat-form-field)</mat-label>\n <input matInput value=\"Photogrammetry\" />\n <mat-hint>Material fields sit in the same grid.</mat-hint>\n </mat-form-field>\n <div class=\"rb-field rb-form-grid__full\">\n <span id=\"showcase-field-scopes\" class=\"rb-field__label\"\n >Granted scopes (mat-chip-grid)</span\n >\n @if (scopes().length > 0) {\n <mat-chip-grid aria-labelledby=\"showcase-field-scopes\">\n @for (scope of scopes(); track scope) {\n <mat-chip-row (removed)=\"removeScope(scope)\">\n {{ scope }}\n <button matChipRemove [attr.aria-label]=\"'Remove ' + scope\">\n <mat-icon aria-hidden=\"true\">close</mat-icon>\n </button>\n </mat-chip-row>\n }\n </mat-chip-grid>\n } @else {\n <p class=\"rb-field__hint\">No scopes granted.</p>\n }\n <p class=\"rb-field__hint\">\n Remove buttons are at least 24px on fine pointers and 44px on coarse pointers.\n @if (scopes().length < defaultScopes.length) {\n <button matButton type=\"button\" (click)=\"resetScopes()\">Restore scopes</button>\n }\n </p>\n </div>\n </form>\n\n <h3 class=\"showcase__subheading\">Tables</h3>\n <p class=\"showcase__lead\">\n Below the <code>md</code> breakpoint each row becomes a stacked record labelled from\n <code>data-label</code>.\n </p>\n <div class=\"rb-table-scroll\">\n <table class=\"rb-table\">\n <caption class=\"rb-visually-hidden\">\n Registered applications\n </caption>\n <thead>\n <tr>\n <th scope=\"col\">Name</th>\n <th scope=\"col\">Client ID</th>\n <th scope=\"col\">Status</th>\n <th scope=\"col\">Updated</th>\n <th scope=\"col\"><span class=\"rb-visually-hidden\">Actions</span></th>\n </tr>\n </thead>\n <tbody>\n @for (row of tableRows; track row.clientId) {\n <tr>\n <td data-label=\"Name\">{{ row.name }}</td>\n <td data-label=\"Client ID\" class=\"showcase__cell-id\">\n <rb-technical-value [value]=\"row.clientId\" />\n </td>\n <td data-label=\"Status\">\n <rb-status-chip\n [tone]=\"row.status.tone\"\n [label]=\"row.status.label\"\n size=\"compact\"\n />\n </td>\n <td data-label=\"Updated\">{{ row.updated }}</td>\n <td>\n <div class=\"rb-table__actions\">\n <button\n matIconButton\n type=\"button\"\n [attr.aria-label]=\"'Edit ' + row.name\"\n [matTooltip]=\"'Edit ' + row.name\"\n >\n <mat-icon aria-hidden=\"true\">edit</mat-icon>\n </button>\n <button\n matIconButton\n color=\"warn\"\n type=\"button\"\n [attr.aria-label]=\"'Delete ' + row.name\"\n [matTooltip]=\"'Delete ' + row.name\"\n >\n <mat-icon aria-hidden=\"true\">delete</mat-icon>\n </button>\n </div>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <div class=\"showcase__grid\">\n <div class=\"rb-table-scroll\">\n <table class=\"rb-table\" aria-busy=\"true\">\n <caption class=\"rb-visually-hidden\">\n Applications, loading\n </caption>\n <thead>\n <tr>\n <th scope=\"col\">Name</th>\n <th scope=\"col\">Status</th>\n </tr>\n </thead>\n <tbody>\n @for (row of [1, 2, 3]; track row) {\n <tr class=\"rb-table__skeleton-row\" aria-hidden=\"true\">\n <td><span class=\"rb-skeleton\"></span></td>\n <td><span class=\"rb-skeleton\"></span></td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n <div class=\"rb-table-scroll\">\n <table class=\"rb-table\">\n <caption class=\"rb-visually-hidden\">\n Applications matching “rover”\n </caption>\n <thead>\n <tr>\n <th scope=\"col\">Name</th>\n <th scope=\"col\">Status</th>\n </tr>\n </thead>\n <tbody>\n <tr class=\"rb-table__empty-row\">\n <td colspan=\"2\">No applications match “rover”.</td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n\n <p class=\"showcase__lead\">\n <code>mat-table</code> with <code>rb-table--inline</code>: the grid is kept on phones and\n scrolls inside <code>rb-table-scroll</code>, which is focusable so keyboard users can scroll\n it.\n </p>\n <div\n class=\"rb-table-scroll\"\n role=\"region\"\n aria-label=\"Registered applications, scrollable\"\n tabindex=\"0\"\n >\n <table mat-table class=\"rb-table rb-table--inline\" [dataSource]=\"tableRows\">\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef scope=\"col\">Name</th>\n <td mat-cell *matCellDef=\"let row\" data-label=\"Name\">{{ row.name }}</td>\n </ng-container>\n <ng-container matColumnDef=\"clientId\">\n <th mat-header-cell *matHeaderCellDef scope=\"col\">Client ID</th>\n <td mat-cell *matCellDef=\"let row\" data-label=\"Client ID\">\n <rb-technical-value [value]=\"row.clientId\" />\n </td>\n </ng-container>\n <ng-container matColumnDef=\"status\">\n <th mat-header-cell *matHeaderCellDef scope=\"col\">Status</th>\n <td mat-cell *matCellDef=\"let row\" data-label=\"Status\">\n <rb-status-chip [tone]=\"row.status.tone\" [label]=\"row.status.label\" size=\"compact\" />\n </td>\n </ng-container>\n <ng-container matColumnDef=\"updated\">\n <th mat-header-cell *matHeaderCellDef scope=\"col\">Updated</th>\n <td mat-cell *matCellDef=\"let row\" data-label=\"Updated\">{{ row.updated }}</td>\n </ng-container>\n <tr mat-header-row *matHeaderRowDef=\"tableColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: tableColumns\"></tr>\n <tr *matNoDataRow>\n <td [attr.colspan]=\"tableColumns.length\">No applications.</td>\n </tr>\n </table>\n </div>\n\n <h3 class=\"showcase__subheading\">Layout</h3>\n <div class=\"rb-master-detail\">\n <rb-panel\n title=\"rb-master-detail\"\n description=\"List on the left, detail on the right.\"\n variant=\"soft\"\n [headingLevel]=\"4\"\n >\n <div class=\"rb-stack rb-stack--sm\">\n <span class=\"rb-skeleton\" aria-hidden=\"true\"></span>\n <span class=\"rb-skeleton\" aria-hidden=\"true\"></span>\n <span class=\"rb-skeleton\" aria-hidden=\"true\"></span>\n </div>\n </rb-panel>\n <rb-panel title=\"Detail\" description=\"Stacks below the xl breakpoint.\" [headingLevel]=\"4\">\n <div class=\"rb-stack\">\n <dl class=\"rb-meta-grid\">\n <div class=\"rb-meta-grid__item\">\n <dt class=\"rb-meta-grid__label\">Owner</dt>\n <dd class=\"rb-meta-grid__value\">Platform team</dd>\n </div>\n <div class=\"rb-meta-grid__item\">\n <dt class=\"rb-meta-grid__label\">Created</dt>\n <dd class=\"rb-meta-grid__value\">3 Mar 2026</dd>\n </div>\n <div class=\"rb-meta-grid__item\">\n <dt class=\"rb-meta-grid__label\">Scopes</dt>\n <dd class=\"rb-meta-grid__value\">openid profile</dd>\n </div>\n </dl>\n <span class=\"rb-skeleton rb-skeleton--heading\" aria-hidden=\"true\"></span>\n <span class=\"rb-skeleton rb-skeleton--control\" aria-hidden=\"true\"></span>\n <div class=\"rb-action-group rb-action-group--between rb-action-group--mobile-stack\">\n <button matButton color=\"warn\" type=\"button\">Delete</button>\n <div class=\"rb-action-group rb-action-group--end rb-action-group--mobile-stack\">\n <button matButton=\"outlined\" type=\"button\">Cancel</button>\n <button matButton=\"filled\" type=\"button\">Save changes</button>\n </div>\n </div>\n </div>\n </rb-panel>\n </div>\n\n <h3 class=\"showcase__subheading\">Text and landing</h3>\n <p class=\"showcase__lead\">\n The hero, tags and icon tiles of the public home pages, and the two uppercase label roles.\n </p>\n <div class=\"rb-hero\">\n <div class=\"rb-hero__copy\">\n <p class=\"rb-eyebrow\">\n <mat-icon aria-hidden=\"true\">deployed_code</mat-icon>\n rb-eyebrow\n </p>\n <p class=\"rb-hero__title\">One standard for every RB Labs app</p>\n <p class=\"rb-hero__lead\">\n <code>.rb-hero__title</code> uses the hero type role; <code>.rb-hero__lead</code> sits\n under it. Below the lg breakpoint the media column moves under the copy.\n </p>\n <ul class=\"rb-tag-list\" aria-label=\"Stack\">\n <li>Angular</li>\n <li>Angular Material 3</li>\n <li>WCAG 2.2 AA</li>\n </ul>\n </div>\n <rb-panel title=\"Media column\" variant=\"soft\" [headingLevel]=\"4\">\n <ul class=\"showcase__icon-tiles\">\n @for (tile of iconTiles; track tile.label) {\n <li>\n <span class=\"rb-icon-tile\" aria-hidden=\"true\">\n <mat-icon>{{ tile.icon }}</mat-icon>\n </span>\n <span>\n <span class=\"rb-overline\">{{ tile.overline }}</span>\n <a class=\"rb-table__link\" routerLink=\".\" fragment=\"showcase-classes\">{{\n tile.label\n }}</a>\n </span>\n </li>\n }\n </ul>\n </rb-panel>\n </div>\n </section>\n </main>\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"rblabs-dev-axiom-showcase.mjs","names":[],"sources":["../../../projects/axiom/showcase/src/shell-example/shell-example.ts","../../../projects/axiom/showcase/src/shell-example/shell-example.html","../../../projects/axiom/showcase/src/design-system-showcase.ts","../../../projects/axiom/showcase/src/design-system-showcase.html"],"sourcesContent":["import { NgTemplateOutlet } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { MatButton, MatIconButton } from '@angular/material/button';\nimport { MatIcon } from '@angular/material/icon';\nimport { MatTooltip } from '@angular/material/tooltip';\nimport { ActivatedRoute, RouterLink } from '@angular/router';\nimport { map } from 'rxjs';\n\nimport {\n RbAppShellComponent,\n RbCentralSignInComponent,\n RbPageHeaderComponent,\n RbPanelComponent,\n RbPublicShellComponent,\n} from '@rblabs-dev/axiom';\n\nexport type RbShellExampleKind = 'app-sidenav' | 'app-modules' | 'public' | 'sign-in';\n\nexport const RB_SHELL_EXAMPLE_KINDS: readonly RbShellExampleKind[] = [\n 'app-sidenav',\n 'app-modules',\n 'public',\n 'sign-in',\n];\n\ninterface ExampleLink {\n id: string;\n label: string;\n icon: string;\n}\n\nconst SIDENAV_SECTIONS: { label: string; links: ExampleLink[] }[] = [\n { label: 'Workspace', links: [{ id: 'overview', label: 'Overview', icon: 'space_dashboard' }] },\n {\n label: 'Administration',\n links: [\n { id: 'users', label: 'Users', icon: 'group' },\n { id: 'applications', label: 'Applications', icon: 'apps' },\n { id: 'services', label: 'Services', icon: 'dns' },\n { id: 'audit', label: 'Registry audit', icon: 'history' },\n ],\n },\n];\n\nconst MODULE_LINKS: ExampleLink[] = [\n { id: 'launch', label: 'Launch', icon: 'add_box' },\n { id: 'datasets', label: 'Datasets', icon: 'inbox' },\n { id: 'jobs', label: 'Jobs', icon: 'autorenew' },\n { id: 'artifacts', label: 'Artifacts', icon: 'deployed_code' },\n];\n\n/**\n * Full-page shell examples for the showcase (`/design-system?example=<kind>`), so each shell is\n * shown in a document of its own: with its own landmarks, at the real viewport width.\n */\n@Component({\n selector: 'rb-showcase-shell-example',\n imports: [\n MatButton,\n MatIcon,\n MatIconButton,\n MatTooltip,\n NgTemplateOutlet,\n RbAppShellComponent,\n RbCentralSignInComponent,\n RbPageHeaderComponent,\n RbPanelComponent,\n RbPublicShellComponent,\n RouterLink,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './shell-example.html',\n styleUrl: './shell-example.scss',\n})\nexport class RbShellExampleComponent {\n readonly kind = input.required<RbShellExampleKind>();\n\n protected readonly sidenavSections = SIDENAV_SECTIONS;\n protected readonly moduleLinks = MODULE_LINKS;\n\n private readonly section = toSignal(\n inject(ActivatedRoute).queryParamMap.pipe(map((params) => params.get('section'))),\n { initialValue: null },\n );\n\n /** The current link: the `section` query parameter, or the first link of the example. */\n protected readonly current = computed(() => {\n const links =\n this.kind() === 'app-modules'\n ? MODULE_LINKS\n : SIDENAV_SECTIONS.flatMap((section) => section.links);\n return links.find((link) => link.id === this.section()) ?? links[0];\n });\n}\n","@switch (kind()) {\n @case ('app-sidenav') {\n <rb-app-shell appName=\"Admin\" homeLink=\"/design-system\" navigation=\"sidenav\">\n <span rbShellTitle>Administration workspace</span>\n <button\n rbShellActions\n matIconButton\n type=\"button\"\n aria-label=\"Sign out\"\n matTooltip=\"Sign out\"\n >\n <mat-icon aria-hidden=\"true\">logout</mat-icon>\n </button>\n\n <nav rbShellSidenav aria-label=\"Administration\">\n @for (section of sidenavSections; track section.label) {\n <h2 class=\"rb-shell-nav-heading\">{{ section.label }}</h2>\n @for (link of section.links; track link.id) {\n <a\n class=\"rb-shell-nav-link\"\n routerLink=\".\"\n [queryParams]=\"{ example: kind(), section: link.id }\"\n [attr.aria-current]=\"current().id === link.id ? 'page' : null\"\n >\n <mat-icon aria-hidden=\"true\">{{ link.icon }}</mat-icon>\n <span>{{ link.label }}</span>\n </a>\n }\n }\n </nav>\n\n <ng-container [ngTemplateOutlet]=\"page\" />\n </rb-app-shell>\n }\n @case ('app-modules') {\n <rb-app-shell appName=\"Photogrammetry\" homeLink=\"/design-system\" navigation=\"modules\">\n <span rbShellTitle>Workspace · {{ current().label }}</span>\n <button\n rbShellActions\n matIconButton\n type=\"button\"\n aria-label=\"Sign out\"\n matTooltip=\"Sign out\"\n >\n <mat-icon aria-hidden=\"true\">logout</mat-icon>\n </button>\n\n <nav rbShellModuleNav aria-label=\"Workspace modules\">\n @for (link of moduleLinks; track link.id) {\n <a\n class=\"rb-shell-nav-link\"\n routerLink=\".\"\n [queryParams]=\"{ example: kind(), section: link.id }\"\n [attr.aria-current]=\"current().id === link.id ? 'page' : null\"\n >\n <mat-icon aria-hidden=\"true\">{{ link.icon }}</mat-icon>\n <span>{{ link.label }}</span>\n </a>\n }\n </nav>\n\n <span rbShellFooter>\n <mat-icon class=\"shell-example__footer-icon\" aria-hidden=\"true\">lock</mat-icon>\n Protected access\n </span>\n\n <ng-container [ngTemplateOutlet]=\"page\" />\n </rb-app-shell>\n }\n @case ('public') {\n <rb-public-shell appName=\"Auth\" homeLink=\"/design-system\">\n <a rbShellFooter routerLink=\"/design-system\">Design system</a>\n\n <section class=\"shell-example__card\" aria-labelledby=\"shell-example-sign-in\">\n <h1 id=\"shell-example-sign-in\" class=\"shell-example__card-title\">Sign in to RB Labs</h1>\n <p class=\"shell-example__card-text\">One account for Admin, Rover and Photogrammetry.</p>\n <form class=\"rb-stack\" (submit)=\"$event.preventDefault()\">\n <div class=\"rb-field\">\n <label class=\"rb-field__label\" for=\"shell-example-email\">Email</label>\n <input\n id=\"shell-example-email\"\n class=\"rb-field__control\"\n type=\"email\"\n autocomplete=\"email\"\n />\n </div>\n <div class=\"rb-field\">\n <label class=\"rb-field__label\" for=\"shell-example-password\">Password</label>\n <input\n id=\"shell-example-password\"\n class=\"rb-field__control\"\n type=\"password\"\n autocomplete=\"current-password\"\n />\n </div>\n <button matButton=\"filled\" type=\"submit\">Sign in</button>\n </form>\n </section>\n </rb-public-shell>\n }\n @case ('sign-in') {\n <rb-public-shell appName=\"Admin\" homeLink=\"/design-system\">\n <a rbShellFooter routerLink=\"/design-system\">Design system</a>\n\n <rb-central-sign-in\n appName=\"Admin\"\n eyebrow=\"Administration workspace\"\n note=\"Restricted to active RB Labs administrators.\"\n backLink=\"/design-system\"\n />\n </rb-public-shell>\n }\n}\n\n<ng-template #page>\n <div class=\"rb-stack\">\n <rb-page-header\n eyebrow=\"Shell example\"\n [title]=\"current().label\"\n description=\"Placeholder content inside the shell's content column.\"\n />\n <div class=\"shell-example__grid\">\n <rb-panel title=\"Content column\" [headingLevel]=\"2\">\n <p class=\"shell-example__text\">\n The column is centered and stops at <code>--rb-size-content-max</code>. Its gutter is\n <code>--rb-space-page</code>.\n </p>\n </rb-panel>\n <rb-panel title=\"Navigation\" variant=\"soft\" [headingLevel]=\"2\">\n <p class=\"shell-example__text\">\n Choose another item: the current one carries <code>aria-current=\"page\"</code>, and below\n <code>lg</code> the drawer closes on navigation.\n </p>\n </rb-panel>\n </div>\n <rb-panel title=\"Recent activity\" [headingLevel]=\"2\">\n <button rbActions matButton type=\"button\">\n <mat-icon aria-hidden=\"true\">refresh</mat-icon>\n Refresh\n </button>\n <div class=\"rb-stack rb-stack--sm\" aria-hidden=\"true\">\n @for (row of [1, 2, 3, 4, 5, 6]; track row) {\n <span class=\"rb-skeleton\"></span>\n }\n </div>\n </rb-panel>\n </div>\n</ng-template>\n","import { ChangeDetectionStrategy, Component, computed, inject, signal } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { MatButton, MatIconButton } from '@angular/material/button';\nimport { MatChipGrid, MatChipRemove, MatChipRow } from '@angular/material/chips';\nimport { MatFormField, MatHint, MatLabel } from '@angular/material/form-field';\nimport { MatIcon } from '@angular/material/icon';\nimport { MatInput } from '@angular/material/input';\nimport { MatTableModule } from '@angular/material/table';\nimport { MatTooltip } from '@angular/material/tooltip';\nimport { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';\nimport { ActivatedRoute, RouterLink } from '@angular/router';\nimport { map } from 'rxjs';\n\nimport {\n RB_DESIGN_SYSTEM_VERSION,\n RbBrandLockupComponent,\n RbConfirmDialogData,\n RbConfirmDialogResult,\n RbConfirmDialogService,\n RbEmptyStateComponent,\n RbMessageComponent,\n RbMessageTone,\n RbMonogramComponent,\n RbPageHeaderComponent,\n RbPageHeaderMetric,\n RbPanelComponent,\n RbStatusChipComponent,\n RbStatusTone,\n RbTechnicalValueComponent,\n RbThemeToggleComponent,\n ThemeService,\n} from '@rblabs-dev/axiom';\n\nimport {\n RB_SHELL_EXAMPLE_KINDS,\n RbShellExampleComponent,\n RbShellExampleKind,\n} from './shell-example/shell-example';\n\ninterface ColorGroup {\n title: string;\n roles: { token: string; on?: string }[];\n}\n\nconst COLOR_GROUPS: ColorGroup[] = [\n {\n title: 'Primary and tertiary',\n roles: [\n { token: 'primary', on: 'on-primary' },\n { token: 'primary-hover', on: 'on-primary' },\n { token: 'primary-soft', on: 'on-primary-soft' },\n { token: 'tertiary', on: 'on-tertiary' },\n ],\n },\n {\n title: 'Surfaces',\n roles: [\n { token: 'bg', on: 'text' },\n { token: 'surface-sunken', on: 'text' },\n { token: 'surface', on: 'text' },\n { token: 'surface-raised', on: 'text' },\n { token: 'surface-hover', on: 'text' },\n { token: 'surface-nav', on: 'text' },\n { token: 'control', on: 'text-strong' },\n ],\n },\n {\n title: 'Text and borders',\n roles: [\n { token: 'text-strong' },\n { token: 'text' },\n { token: 'text-muted' },\n { token: 'text-subtle' },\n { token: 'text-disabled' },\n { token: 'border' },\n { token: 'border-strong' },\n { token: 'outline' },\n { token: 'focus' },\n ],\n },\n {\n title: 'Status',\n roles: [\n { token: 'info', on: 'on-info' },\n { token: 'info-soft', on: 'info' },\n { token: 'success', on: 'on-success' },\n { token: 'success-soft', on: 'success' },\n { token: 'warning', on: 'on-warning' },\n { token: 'warning-soft', on: 'warning' },\n { token: 'danger', on: 'on-danger' },\n { token: 'danger-soft', on: 'danger' },\n ],\n },\n];\n\nconst TYPE_ROLES = [\n 'hero',\n 'page-title',\n 'display-small',\n 'headline-large',\n 'headline-medium',\n 'headline-small',\n 'title-large',\n 'title-medium',\n 'title-small',\n 'body-large',\n 'body-medium',\n 'body-small',\n 'label-large',\n 'label-medium',\n 'label-small',\n];\n\nconst MESSAGE_SAMPLES: { tone: RbMessageTone; title: string; body: string }[] = [\n { tone: 'neutral', title: 'Read-only', body: 'Registry entries are managed by the deploy job.' },\n { tone: 'info', title: 'Sync scheduled', body: 'The registry syncs again in 5 minutes.' },\n { tone: 'success', title: 'Application saved', body: 'Redirect URIs take effect immediately.' },\n { tone: 'warning', title: 'Key expires soon', body: 'Rotate it before 1 November 2026.' },\n { tone: 'danger', title: 'Sync failed', body: 'The registry could not be reached.' },\n];\n\nconst CHIP_SAMPLES: { tone: RbStatusTone; label: string }[] = [\n { tone: 'active', label: 'Running' },\n { tone: 'success', label: 'Active' },\n { tone: 'warning', label: 'Disabled' },\n { tone: 'danger', label: 'Revoked' },\n { tone: 'neutral', label: 'Draft' },\n];\n\nexport interface ShowcaseRow {\n name: string;\n clientId: string;\n status: { tone: RbStatusTone; label: string };\n updated: string;\n}\n\nconst TABLE_ROWS: ShowcaseRow[] = [\n {\n name: 'RB Labs Auth',\n clientId: 'app_01HZX9K4T7Q2M8R5W3N6B1C0DE',\n status: { tone: 'success', label: 'Active' },\n updated: '19 Oct 2026',\n },\n {\n name: 'Photogrammetry workspace',\n clientId: 'app_01J2B7F9P4L6X3V8S1D5G0HAKM',\n status: { tone: 'warning', label: 'Disabled' },\n updated: '2 Oct 2026',\n },\n {\n name: 'Rover control',\n clientId: 'app_01J3C1A8E5N2Y7U4Q9T6R0ZWXP',\n status: { tone: 'danger', label: 'Revoked' },\n updated: '14 Sep 2026',\n },\n];\n\nconst SHELL_EXAMPLES: { kind: RbShellExampleKind; title: string; description: string }[] = [\n {\n kind: 'app-sidenav',\n title: 'App shell with sidenav',\n description: 'Admin-style navigation rail; an overlay drawer below lg.',\n },\n {\n kind: 'app-modules',\n title: 'App shell with module nav',\n description: 'Photogrammetry-style modules; a row below lg and a tab bar below sm.',\n },\n {\n kind: 'public',\n title: 'Public shell',\n description: 'Landing, sign-in and other public pages.',\n },\n {\n kind: 'sign-in',\n title: 'Central sign-in',\n description: 'The page that hands off to RB Labs Auth, alone in the public shell.',\n },\n];\n\nconst DEFAULT_SCOPES = ['openid', 'profile', 'email', 'offline_access'];\n\nconst ICON_TILES = [\n { icon: 'group', overline: 'rb-icon-tile', label: 'Users and roles' },\n { icon: 'apps', overline: 'rb-overline', label: 'Registered applications' },\n { icon: 'history', overline: 'rb-table__link', label: 'Registry audit' },\n];\n\nconst STAGE_METRICS = [\n { label: 'Bitrate', value: '2.4 Mbps' },\n { label: 'FPS', value: '30' },\n { label: 'Resolution', value: '1280 × 720' },\n];\n\nconst MONOGRAM_SIZES = [\n { token: '--rb-size-monogram', value: 'var(--rb-size-monogram)' },\n { token: '--rb-size-monogram-sm', value: 'var(--rb-size-monogram-sm)' },\n { token: 'favicon (16px)', value: 'var(--rb-size-icon-sm)' },\n];\n\nconst BUTTON_ROLES = [\n { role: 'Primary', markup: 'matButton=\"filled\"', legacy: 'mat-flat-button' },\n { role: 'Secondary', markup: 'matButton=\"outlined\"', legacy: 'mat-stroked-button' },\n { role: 'Tertiary', markup: 'matButton', legacy: 'mat-button' },\n {\n role: 'Danger',\n markup: 'matButton=\"filled\" color=\"warn\"',\n legacy: 'mat-flat-button color=\"warn\"',\n },\n { role: 'Icon', markup: 'matIconButton', legacy: 'mat-icon-button' },\n];\n\n/**\n * Living reference of the kit (docs/design-system.md): tokens, type, spacing, shape, elevation,\n * Material components, the content components and the kit style classes, in the active theme.\n */\n@Component({\n selector: 'rb-design-system-showcase',\n imports: [\n MatButton,\n MatChipGrid,\n MatChipRemove,\n MatChipRow,\n MatFormField,\n MatHint,\n MatIcon,\n MatIconButton,\n MatInput,\n MatLabel,\n MatTableModule,\n MatTooltip,\n RbBrandLockupComponent,\n RbEmptyStateComponent,\n RbMessageComponent,\n RbMonogramComponent,\n RbPageHeaderComponent,\n RbPanelComponent,\n RbStatusChipComponent,\n RbShellExampleComponent,\n RbTechnicalValueComponent,\n RbThemeToggleComponent,\n RouterLink,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './design-system-showcase.html',\n styleUrl: './design-system-showcase.scss',\n // `rb-scope` activates the kit in apps that still include it scoped.\n host: { class: 'rb-scope', '[class.showcase--example]': 'example() !== null' },\n})\nexport class DesignSystemShowcaseComponent {\n protected readonly theme = inject(ThemeService);\n private readonly confirmDialog = inject(RbConfirmDialogService);\n\n protected readonly version = RB_DESIGN_SYSTEM_VERSION;\n protected readonly colorGroups = COLOR_GROUPS;\n protected readonly typeRoles = TYPE_ROLES;\n protected readonly spaceSteps = ['1', '2', '3', '4', '5', '6', '8', '10', '12', '16'];\n protected readonly radii = ['xs', 'sm', 'md', 'lg', 'xl', 'full'];\n protected readonly elevations = ['0', '1', '2', '3'];\n protected readonly messageSamples = MESSAGE_SAMPLES;\n protected readonly chipSamples = CHIP_SAMPLES;\n protected readonly tableRows = TABLE_ROWS;\n protected readonly tableColumns = ['name', 'clientId', 'status', 'updated'];\n protected readonly buttonRoles = BUTTON_ROLES;\n protected readonly monogramSizes = MONOGRAM_SIZES;\n protected readonly iconTiles = ICON_TILES;\n protected readonly stageMetrics = STAGE_METRICS;\n\n /** `?example=<kind>` renders one shell example full page instead of the reference. */\n protected readonly example = toSignal(\n inject(ActivatedRoute).queryParamMap.pipe(\n map((params) => {\n const kind = params.get('example') as RbShellExampleKind | null;\n return kind && RB_SHELL_EXAMPLE_KINDS.includes(kind) ? kind : null;\n }),\n ),\n { initialValue: null },\n );\n\n // Relative to the base href: the showcase is routed at `design-system` in every app. The frames\n // are re-created when the theme changes (see the template), so they pick up a stored choice.\n private readonly sanitizer = inject(DomSanitizer);\n protected readonly shellExamples = SHELL_EXAMPLES.map((example) => ({\n ...example,\n src: this.sanitizer.bypassSecurityTrustResourceUrl(\n `design-system?example=${example.kind}`,\n ) as SafeResourceUrl,\n }));\n\n protected readonly headerMetrics = computed<RbPageHeaderMetric[]>(() => [\n { label: 'Kit version', value: this.version },\n { label: 'Theme', value: this.theme.isDark() ? 'Dark' : 'Light' },\n { label: 'Components', value: 12 },\n { label: 'Contrast', value: 'WCAG 2.2 AA', tone: 'success' },\n ]);\n\n protected readonly defaultScopes = DEFAULT_SCOPES;\n protected readonly scopes = signal(DEFAULT_SCOPES);\n\n /** Mounted from the start so the result of each dialog is announced (section 9.5). */\n protected readonly dialogResult = signal('No confirmation opened yet.');\n protected readonly dialogResultTone = signal<RbMessageTone>('neutral');\n\n protected async openConfirm(kind: 'primary' | 'danger' | 'reason'): Promise<void> {\n const data: RbConfirmDialogData =\n kind === 'primary'\n ? {\n title: 'Publish release notes?',\n description: 'Every signed-in user sees the notes on their next visit.',\n confirmLabel: 'Publish notes',\n }\n : {\n title: 'Revoke this credential?',\n description: 'Clients using it stop working immediately. This cannot be undone.',\n confirmLabel: 'Revoke credential',\n tone: 'danger',\n ...(kind === 'reason'\n ? { reason: { label: 'Reason for revoking', hint: 'Recorded in the audit log.' } }\n : {}),\n };\n this.showResult(await this.confirmDialog.confirm(data));\n }\n\n protected removeScope(scope: string): void {\n this.scopes.update((scopes) => scopes.filter((item) => item !== scope));\n }\n\n protected resetScopes(): void {\n this.scopes.set(DEFAULT_SCOPES);\n }\n\n private showResult(result: RbConfirmDialogResult): void {\n this.dialogResultTone.set(result.confirmed ? 'success' : 'neutral');\n this.dialogResult.set(\n result.confirmed\n ? `Confirmed${result.reason ? ` with reason “${result.reason}”` : ''}.`\n : 'Cancelled. Nothing changed.',\n );\n }\n}\n","@if (example(); as kind) {\n <rb-showcase-shell-example [kind]=\"kind\" />\n} @else {\n <!-- Focuses main itself: under <base href=\"/\"> the bare fragment would load the root route. -->\n <a\n class=\"rb-skip-link\"\n href=\"#rb-showcase-main\"\n (click)=\"$event.preventDefault(); showcaseMain.focus()\"\n >Skip to main content</a\n >\n\n <main\n #showcaseMain\n id=\"rb-showcase-main\"\n class=\"showcase__main\"\n tabindex=\"-1\"\n aria-label=\"Design system reference\"\n >\n <rb-page-header\n class=\"showcase__header\"\n eyebrow=\"RB Labs Design System\"\n title=\"Foundations and components\"\n [description]=\"\n theme.preference() === null\n ? 'Tokens, Material components and the content kit, following the operating system theme.'\n : 'Tokens, Material components and the content kit.'\n \"\n [metrics]=\"headerMetrics()\"\n >\n <rb-theme-toggle rbActions />\n </rb-page-header>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-color\">\n <h2 id=\"showcase-color\" class=\"showcase__heading\">Color roles</h2>\n @for (group of colorGroups; track group.title) {\n <h3 class=\"showcase__subheading\">{{ group.title }}</h3>\n <ul class=\"showcase__swatches\">\n @for (role of group.roles; track role.token) {\n <li class=\"showcase__swatch\">\n <span\n class=\"showcase__chip\"\n [style.background-color]=\"'var(--rb-color-' + role.token + ')'\"\n [style.color]=\"role.on ? 'var(--rb-color-' + role.on + ')' : null\"\n aria-hidden=\"true\"\n >{{ role.on ? 'Aa' : '' }}</span\n >\n <code>--rb-color-{{ role.token }}</code>\n @if (role.on) {\n <span class=\"showcase__note\">text: {{ role.on }}</span>\n }\n </li>\n }\n </ul>\n }\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-type\">\n <h2 id=\"showcase-type\" class=\"showcase__heading\">Type scale</h2>\n <ul class=\"showcase__list\">\n @for (role of typeRoles; track role) {\n <li class=\"showcase__type-row\">\n <code>{{ role }}</code>\n <span\n class=\"showcase__type-sample\"\n [style.font]=\"'var(--rb-type-' + role + ')'\"\n [style.letter-spacing]=\"'var(--rb-type-' + role + '-tracking)'\"\n >Operate RB Labs</span\n >\n </li>\n }\n </ul>\n <p class=\"showcase__mono\">ui-monospace · usr_01HZX9 · sha256:2b4f69c9</p>\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-space\">\n <h2 id=\"showcase-space\" class=\"showcase__heading\">Spacing</h2>\n <ul class=\"showcase__list\">\n @for (step of spaceSteps; track step) {\n <li class=\"showcase__space-row\">\n <code>--rb-space-{{ step }}</code>\n <span\n class=\"showcase__space-bar\"\n [style.inline-size]=\"'var(--rb-space-' + step + ')'\"\n aria-hidden=\"true\"\n ></span>\n </li>\n }\n </ul>\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-shape\">\n <h2 id=\"showcase-shape\" class=\"showcase__heading\">Radius and elevation</h2>\n <ul class=\"showcase__tiles\">\n @for (radius of radii; track radius) {\n <li class=\"showcase__tile\" [style.border-radius]=\"'var(--rb-radius-' + radius + ')'\">\n <code>radius-{{ radius }}</code>\n </li>\n }\n </ul>\n <ul class=\"showcase__tiles\">\n @for (level of elevations; track level) {\n <li\n class=\"showcase__tile showcase__tile--raised\"\n [style.box-shadow]=\"'var(--rb-elevation-' + level + ')'\"\n >\n <code>elevation-{{ level }}</code>\n </li>\n }\n </ul>\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-material\">\n <h2 id=\"showcase-material\" class=\"showcase__heading\">Buttons</h2>\n <p class=\"showcase__lead\">\n There is no kit button component. Each role maps to an Angular Material button variant.\n </p>\n <div class=\"rb-table-scroll\">\n <table class=\"rb-table\">\n <caption class=\"rb-visually-hidden\">\n Button roles and their Material markup\n </caption>\n <thead>\n <tr>\n <th scope=\"col\">Role</th>\n <th scope=\"col\">Markup</th>\n <th scope=\"col\">Equivalent selector</th>\n </tr>\n </thead>\n <tbody>\n @for (button of buttonRoles; track button.role) {\n <tr>\n <td data-label=\"Role\">{{ button.role }}</td>\n <td data-label=\"Markup\">\n <code>{{ button.markup }}</code>\n </td>\n <td data-label=\"Equivalent selector\">\n <code>{{ button.legacy }}</code>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n <div class=\"rb-action-group\">\n <button matButton=\"filled\" type=\"button\">Primary</button>\n <button matButton=\"outlined\" type=\"button\">Secondary</button>\n <button matButton type=\"button\">Tertiary</button>\n <button matButton=\"tonal\" type=\"button\">Tonal</button>\n <button matButton=\"filled\" type=\"button\" disabled>Disabled</button>\n </div>\n <div class=\"rb-action-group\">\n <button matButton=\"filled\" color=\"warn\" type=\"button\">\n <mat-icon aria-hidden=\"true\">delete</mat-icon>\n Delete\n </button>\n <button matButton=\"outlined\" color=\"warn\" type=\"button\">Revoke</button>\n <button matButton color=\"warn\" type=\"button\">Remove</button>\n <button\n matIconButton\n color=\"warn\"\n type=\"button\"\n aria-label=\"Delete item\"\n matTooltip=\"Delete item\"\n >\n <mat-icon aria-hidden=\"true\">delete</mat-icon>\n </button>\n <button matIconButton type=\"button\" aria-label=\"Edit item\" matTooltip=\"Edit item\">\n <mat-icon aria-hidden=\"true\">edit</mat-icon>\n </button>\n </div>\n <p class=\"showcase__lead\">\n A link that navigates uses the same variants on an <code>&lt;a&gt;</code>. On coarse\n pointers it reaches the touch target like a button.\n </p>\n <div class=\"rb-action-group\">\n <a matButton=\"filled\" routerLink=\".\" fragment=\"showcase-material\">Primary link</a>\n <a matButton=\"outlined\" routerLink=\".\" fragment=\"showcase-material\">Secondary link</a>\n <a matButton routerLink=\".\" fragment=\"showcase-material\">Tertiary link</a>\n <a matButton=\"tonal\" routerLink=\".\" fragment=\"showcase-material\">Tonal link</a>\n <a\n matIconButton\n routerLink=\".\"\n fragment=\"showcase-material\"\n aria-label=\"Go to buttons\"\n matTooltip=\"Go to buttons\"\n >\n <mat-icon aria-hidden=\"true\">arrow_forward</mat-icon>\n </a>\n </div>\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-shells\">\n <h2 id=\"showcase-shells\" class=\"showcase__heading\">Shells and brand</h2>\n\n <h3 class=\"showcase__subheading\">Brand lockup and monogram</h3>\n <div class=\"showcase__brand-row\">\n <rb-brand-lockup appName=\"Admin\" link=\"/design-system\" />\n <rb-brand-lockup appName=\"Photogrammetry\" link=\"/design-system\" />\n <rb-brand-lockup appName=\"Rover\" link=\"/design-system\" compact />\n </div>\n <ul class=\"showcase__brand-row showcase__brand-row--list\" aria-label=\"Monogram sizes\">\n @for (size of monogramSizes; track size.token) {\n <li class=\"showcase__monogram\">\n <rb-monogram [style.inline-size]=\"size.value\" />\n <code>{{ size.token }}</code>\n </li>\n }\n </ul>\n\n <p class=\"showcase__lead\">\n Each preview is a frame whose width is the viewport, so on narrow screens the sidenav\n becomes a drawer and the module nav a tab bar. The previews are inert, so their landmarks do\n not repeat in this page; open the full page to use an example.\n </p>\n @for (example of shellExamples; track example.kind + theme.theme()) {\n <h3 class=\"showcase__subheading\">{{ example.title }}</h3>\n <p class=\"showcase__lead\">\n {{ example.description }}\n <a routerLink=\".\" [queryParams]=\"{ example: example.kind }\">Open full page</a>\n </p>\n <iframe\n class=\"showcase__frame\"\n [title]=\"example.title + ' example'\"\n [src]=\"example.src\"\n loading=\"lazy\"\n inert\n ></iframe>\n }\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-components\">\n <h2 id=\"showcase-components\" class=\"showcase__heading\">Content components</h2>\n\n <h3 class=\"showcase__subheading\">Page header</h3>\n <p class=\"showcase__lead\">\n The header of this page is an <code>rb-page-header</code> with an eyebrow, description,\n metrics and the theme toggle as its action. A page has exactly one.\n </p>\n\n <h3 class=\"showcase__subheading\">Panel</h3>\n <div class=\"showcase__grid\">\n <rb-panel title=\"Default\" description=\"Cards and grouped content.\" [headingLevel]=\"4\">\n <button rbActions matButton type=\"button\">\n <mat-icon aria-hidden=\"true\">refresh</mat-icon>\n Refresh\n </button>\n <p class=\"showcase__text\">Surface background, border and elevation 1.</p>\n </rb-panel>\n <rb-panel\n title=\"Soft\"\n description=\"Secondary or inset content.\"\n variant=\"soft\"\n [headingLevel]=\"4\"\n >\n <p class=\"showcase__text\">Sunken surface, no shadow.</p>\n </rb-panel>\n <rb-panel\n title=\"Elevated\"\n description=\"Content that floats above the page.\"\n variant=\"elevated\"\n [headingLevel]=\"4\"\n >\n <p class=\"showcase__text\">Raised surface and elevation 2.</p>\n </rb-panel>\n <rb-panel\n title=\"Danger zone\"\n description=\"Destructive settings.\"\n variant=\"danger\"\n [headingLevel]=\"4\"\n >\n <button rbActions matButton=\"outlined\" color=\"warn\" type=\"button\">Delete</button>\n <p class=\"showcase__text\">Danger border and title; the title names the risk.</p>\n </rb-panel>\n </div>\n\n <h3 class=\"showcase__subheading\">Message</h3>\n <div class=\"rb-stack rb-stack--sm\">\n @for (sample of messageSamples; track sample.tone) {\n <rb-message [tone]=\"sample.tone\" [title]=\"sample.title\" live=\"off\">\n {{ sample.body }}\n </rb-message>\n }\n </div>\n\n <h3 class=\"showcase__subheading\">Empty state</h3>\n <div class=\"showcase__grid\">\n <rb-panel title=\"Applications\" [headingLevel]=\"4\">\n <rb-empty-state\n icon=\"apps\"\n title=\"No applications yet\"\n description=\"Register a client to issue credentials for RB Labs sign-in.\"\n actionLabel=\"Register application\"\n [headingLevel]=\"5\"\n />\n </rb-panel>\n <rb-panel title=\"Audit history\" variant=\"soft\" [headingLevel]=\"4\">\n <rb-empty-state\n icon=\"history\"\n title=\"Nothing recorded\"\n description=\"Changes to this application appear here.\"\n layout=\"inline\"\n [headingLevel]=\"5\"\n />\n </rb-panel>\n </div>\n\n <h3 class=\"showcase__subheading\">Status chip</h3>\n <div class=\"rb-action-group\">\n @for (chip of chipSamples; track chip.tone) {\n <rb-status-chip [tone]=\"chip.tone\" [label]=\"chip.label\" />\n }\n </div>\n <div class=\"rb-action-group\">\n @for (chip of chipSamples; track chip.tone) {\n <rb-status-chip [tone]=\"chip.tone\" [label]=\"chip.label\" size=\"compact\" />\n }\n <rb-status-chip label=\"Locked\" icon=\"lock\" size=\"compact\" />\n </div>\n\n <h3 class=\"showcase__subheading\">Technical value</h3>\n <dl class=\"rb-meta-grid\">\n <div class=\"rb-meta-grid__item\">\n <dt class=\"rb-meta-grid__label\">Fits</dt>\n <dd class=\"rb-meta-grid__value\"><rb-technical-value value=\"usr_01HZX9\" /></dd>\n </div>\n <div class=\"rb-meta-grid__item showcase__narrow\">\n <dt class=\"rb-meta-grid__label\">Truncated</dt>\n <dd class=\"rb-meta-grid__value\">\n <rb-technical-value\n value=\"https://auth.rblabs.dev/oauth/callback/photogrammetry-workspace\"\n />\n </dd>\n </div>\n <div class=\"rb-meta-grid__item\">\n <dt class=\"rb-meta-grid__label\">Shortened</dt>\n <dd class=\"rb-meta-grid__value\">\n <rb-technical-value\n value=\"sha256:2b4f69c9d1e0a7b35c8e6f41a9d2c7b0e3f5a8c1d4b7e0f2a5c8d1e4b7a0c3f6\"\n displayValue=\"sha256:2b4f69c9…\"\n />\n </dd>\n </div>\n </dl>\n\n <h3 class=\"showcase__subheading\">Confirmation dialog</h3>\n <div class=\"rb-action-group\">\n <button matButton=\"outlined\" type=\"button\" (click)=\"openConfirm('primary')\">\n Confirm an action\n </button>\n <button matButton=\"outlined\" color=\"warn\" type=\"button\" (click)=\"openConfirm('danger')\">\n Confirm a destructive action\n </button>\n <button matButton=\"outlined\" color=\"warn\" type=\"button\" (click)=\"openConfirm('reason')\">\n Confirm with a reason\n </button>\n </div>\n <rb-message [tone]=\"dialogResultTone()\" title=\"Last result\">{{ dialogResult() }}</rb-message>\n </section>\n\n <section class=\"showcase__section\" aria-labelledby=\"showcase-classes\">\n <h2 id=\"showcase-classes\" class=\"showcase__heading\">Style classes</h2>\n\n <h3 class=\"showcase__subheading\">Forms</h3>\n <form class=\"rb-form-grid\" (submit)=\"$event.preventDefault()\">\n <div class=\"rb-field\">\n <label class=\"rb-field__label\" for=\"showcase-field-name\">Application name</label>\n <input\n id=\"showcase-field-name\"\n class=\"rb-field__control\"\n type=\"text\"\n value=\"Photogrammetry workspace\"\n aria-describedby=\"showcase-field-name-hint\"\n />\n <p id=\"showcase-field-name-hint\" class=\"rb-field__hint\">Shown on the consent screen.</p>\n </div>\n <div class=\"rb-field\">\n <label class=\"rb-field__label\" for=\"showcase-field-environment\">Environment</label>\n <select id=\"showcase-field-environment\" class=\"rb-field__control\">\n <option>Production</option>\n <option>Staging</option>\n </select>\n </div>\n <div class=\"rb-field rb-field--invalid\">\n <label class=\"rb-field__label\" for=\"showcase-field-uri\">Redirect URI</label>\n <input\n id=\"showcase-field-uri\"\n class=\"rb-field__control\"\n type=\"url\"\n value=\"http://photogrammetry\"\n aria-invalid=\"true\"\n aria-describedby=\"showcase-field-uri-error\"\n />\n <p id=\"showcase-field-uri-error\" class=\"rb-field__error\">\n Enter an https:// address, for example https://app.rblabs.dev/callback.\n </p>\n </div>\n <div class=\"rb-field rb-field--disabled\">\n <label class=\"rb-field__label\" for=\"showcase-field-client\">Client ID</label>\n <input\n id=\"showcase-field-client\"\n class=\"rb-field__control\"\n type=\"text\"\n value=\"app_01J2B7F9P4L6X3V8S1D5G0HAKM\"\n disabled\n aria-describedby=\"showcase-field-client-hint\"\n />\n <p id=\"showcase-field-client-hint\" class=\"rb-field__hint\">\n Generated at registration and cannot be changed.\n </p>\n </div>\n <div class=\"rb-field rb-form-grid__full\">\n <label class=\"rb-field__label\" for=\"showcase-field-notes\">Notes</label>\n <textarea\n id=\"showcase-field-notes\"\n class=\"rb-field__control\"\n placeholder=\"Optional context for other administrators\"\n ></textarea>\n </div>\n <mat-form-field appearance=\"outline\">\n <mat-label>Display name (mat-form-field)</mat-label>\n <input matInput value=\"Photogrammetry\" />\n <mat-hint>Material fields sit in the same grid.</mat-hint>\n </mat-form-field>\n <div class=\"rb-field rb-form-grid__full\">\n <span id=\"showcase-field-scopes\" class=\"rb-field__label\"\n >Granted scopes (mat-chip-grid)</span\n >\n @if (scopes().length > 0) {\n <mat-chip-grid aria-labelledby=\"showcase-field-scopes\">\n @for (scope of scopes(); track scope) {\n <mat-chip-row (removed)=\"removeScope(scope)\">\n {{ scope }}\n <button matChipRemove [attr.aria-label]=\"'Remove ' + scope\">\n <mat-icon aria-hidden=\"true\">close</mat-icon>\n </button>\n </mat-chip-row>\n }\n </mat-chip-grid>\n } @else {\n <p class=\"rb-field__hint\">No scopes granted.</p>\n }\n <p class=\"rb-field__hint\">\n Remove buttons are at least 24px on fine pointers and 44px on coarse pointers.\n @if (scopes().length < defaultScopes.length) {\n <button matButton type=\"button\" (click)=\"resetScopes()\">Restore scopes</button>\n }\n </p>\n </div>\n </form>\n\n <h3 class=\"showcase__subheading\">Tables</h3>\n <p class=\"showcase__lead\">\n Below the <code>md</code> breakpoint each row becomes a stacked record labelled from\n <code>data-label</code>.\n </p>\n <div class=\"rb-table-scroll\">\n <table class=\"rb-table\">\n <caption class=\"rb-visually-hidden\">\n Registered applications\n </caption>\n <thead>\n <tr>\n <th scope=\"col\">Name</th>\n <th scope=\"col\">Client ID</th>\n <th scope=\"col\">Status</th>\n <th scope=\"col\">Updated</th>\n <th scope=\"col\"><span class=\"rb-visually-hidden\">Actions</span></th>\n </tr>\n </thead>\n <tbody>\n @for (row of tableRows; track row.clientId) {\n <tr>\n <td data-label=\"Name\">{{ row.name }}</td>\n <td data-label=\"Client ID\" class=\"showcase__cell-id\">\n <rb-technical-value [value]=\"row.clientId\" />\n </td>\n <td data-label=\"Status\">\n <rb-status-chip\n [tone]=\"row.status.tone\"\n [label]=\"row.status.label\"\n size=\"compact\"\n />\n </td>\n <td data-label=\"Updated\">{{ row.updated }}</td>\n <td>\n <div class=\"rb-table__actions\">\n <button\n matIconButton\n type=\"button\"\n [attr.aria-label]=\"'Edit ' + row.name\"\n [matTooltip]=\"'Edit ' + row.name\"\n >\n <mat-icon aria-hidden=\"true\">edit</mat-icon>\n </button>\n <button\n matIconButton\n color=\"warn\"\n type=\"button\"\n [attr.aria-label]=\"'Delete ' + row.name\"\n [matTooltip]=\"'Delete ' + row.name\"\n >\n <mat-icon aria-hidden=\"true\">delete</mat-icon>\n </button>\n </div>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <div class=\"showcase__grid\">\n <div class=\"rb-table-scroll\">\n <table class=\"rb-table\" aria-busy=\"true\">\n <caption class=\"rb-visually-hidden\">\n Applications, loading\n </caption>\n <thead>\n <tr>\n <th scope=\"col\">Name</th>\n <th scope=\"col\">Status</th>\n </tr>\n </thead>\n <tbody>\n @for (row of [1, 2, 3]; track row) {\n <tr class=\"rb-table__skeleton-row\" aria-hidden=\"true\">\n <td><span class=\"rb-skeleton\"></span></td>\n <td><span class=\"rb-skeleton\"></span></td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n <div class=\"rb-table-scroll\">\n <table class=\"rb-table\">\n <caption class=\"rb-visually-hidden\">\n Applications matching “rover”\n </caption>\n <thead>\n <tr>\n <th scope=\"col\">Name</th>\n <th scope=\"col\">Status</th>\n </tr>\n </thead>\n <tbody>\n <tr class=\"rb-table__empty-row\">\n <td colspan=\"2\">No applications match “rover”.</td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n\n <p class=\"showcase__lead\">\n <code>mat-table</code> with <code>rb-table--inline</code>: the grid is kept on phones and\n scrolls inside <code>rb-table-scroll</code>, which is focusable so keyboard users can scroll\n it.\n </p>\n <div\n class=\"rb-table-scroll\"\n role=\"region\"\n aria-label=\"Registered applications, scrollable\"\n tabindex=\"0\"\n >\n <table mat-table class=\"rb-table rb-table--inline\" [dataSource]=\"tableRows\">\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef scope=\"col\">Name</th>\n <td mat-cell *matCellDef=\"let row\" data-label=\"Name\">{{ row.name }}</td>\n </ng-container>\n <ng-container matColumnDef=\"clientId\">\n <th mat-header-cell *matHeaderCellDef scope=\"col\">Client ID</th>\n <td mat-cell *matCellDef=\"let row\" data-label=\"Client ID\">\n <rb-technical-value [value]=\"row.clientId\" />\n </td>\n </ng-container>\n <ng-container matColumnDef=\"status\">\n <th mat-header-cell *matHeaderCellDef scope=\"col\">Status</th>\n <td mat-cell *matCellDef=\"let row\" data-label=\"Status\">\n <rb-status-chip [tone]=\"row.status.tone\" [label]=\"row.status.label\" size=\"compact\" />\n </td>\n </ng-container>\n <ng-container matColumnDef=\"updated\">\n <th mat-header-cell *matHeaderCellDef scope=\"col\">Updated</th>\n <td mat-cell *matCellDef=\"let row\" data-label=\"Updated\">{{ row.updated }}</td>\n </ng-container>\n <tr mat-header-row *matHeaderRowDef=\"tableColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: tableColumns\"></tr>\n <tr *matNoDataRow>\n <td [attr.colspan]=\"tableColumns.length\">No applications.</td>\n </tr>\n </table>\n </div>\n\n <h3 class=\"showcase__subheading\">Layout</h3>\n <div class=\"rb-master-detail\">\n <rb-panel\n title=\"rb-master-detail\"\n description=\"List on the left, detail on the right.\"\n variant=\"soft\"\n [headingLevel]=\"4\"\n >\n <div class=\"rb-stack rb-stack--sm\">\n <span class=\"rb-skeleton\" aria-hidden=\"true\"></span>\n <span class=\"rb-skeleton\" aria-hidden=\"true\"></span>\n <span class=\"rb-skeleton\" aria-hidden=\"true\"></span>\n </div>\n </rb-panel>\n <rb-panel title=\"Detail\" description=\"Stacks below the xl breakpoint.\" [headingLevel]=\"4\">\n <div class=\"rb-stack\">\n <dl class=\"rb-meta-grid\">\n <div class=\"rb-meta-grid__item\">\n <dt class=\"rb-meta-grid__label\">Owner</dt>\n <dd class=\"rb-meta-grid__value\">Platform team</dd>\n </div>\n <div class=\"rb-meta-grid__item\">\n <dt class=\"rb-meta-grid__label\">Created</dt>\n <dd class=\"rb-meta-grid__value\">3 Mar 2026</dd>\n </div>\n <div class=\"rb-meta-grid__item\">\n <dt class=\"rb-meta-grid__label\">Scopes</dt>\n <dd class=\"rb-meta-grid__value\">openid profile</dd>\n </div>\n </dl>\n <span class=\"rb-skeleton rb-skeleton--heading\" aria-hidden=\"true\"></span>\n <span class=\"rb-skeleton rb-skeleton--control\" aria-hidden=\"true\"></span>\n <div class=\"rb-action-group rb-action-group--between rb-action-group--mobile-stack\">\n <button matButton color=\"warn\" type=\"button\">Delete</button>\n <div class=\"rb-action-group rb-action-group--end rb-action-group--mobile-stack\">\n <button matButton=\"outlined\" type=\"button\">Cancel</button>\n <button matButton=\"filled\" type=\"button\">Save changes</button>\n </div>\n </div>\n </div>\n </rb-panel>\n </div>\n\n <h3 class=\"showcase__subheading\">Text and landing</h3>\n <p class=\"showcase__lead\">\n The hero, tags and icon tiles of the public home pages, and the two uppercase label roles.\n </p>\n <div class=\"rb-hero\">\n <div class=\"rb-hero__copy\">\n <p class=\"rb-eyebrow\">\n <mat-icon aria-hidden=\"true\">deployed_code</mat-icon>\n rb-eyebrow\n </p>\n <p class=\"rb-hero__title\">One standard for every RB Labs app</p>\n <p class=\"rb-hero__lead\">\n <code>.rb-hero__title</code> uses the hero type role; <code>.rb-hero__lead</code> sits\n under it. Below the lg breakpoint the media column moves under the copy.\n </p>\n <ul class=\"rb-tag-list\" aria-label=\"Stack\">\n <li>Angular</li>\n <li>Angular Material 3</li>\n <li>WCAG 2.2 AA</li>\n </ul>\n </div>\n <rb-panel title=\"Media column\" variant=\"soft\" [headingLevel]=\"4\">\n <ul class=\"showcase__icon-tiles\">\n @for (tile of iconTiles; track tile.label) {\n <li>\n <span class=\"rb-icon-tile\" aria-hidden=\"true\">\n <mat-icon>{{ tile.icon }}</mat-icon>\n </span>\n <span>\n <span class=\"rb-overline\">{{ tile.overline }}</span>\n <a class=\"rb-table__link\" routerLink=\".\" fragment=\"showcase-classes\">{{\n tile.label\n }}</a>\n </span>\n </li>\n }\n </ul>\n </rb-panel>\n </div>\n <div class=\"showcase__stage\">\n <p class=\"showcase__stage-caption\">\n Over a media stage, labels take <code>.rb-overline--on-stage</code>.\n </p>\n <dl class=\"showcase__stage-list\">\n @for (metric of stageMetrics; track metric.label) {\n <div>\n <dt class=\"rb-overline rb-overline--on-stage\">{{ metric.label }}</dt>\n <dd>{{ metric.value }}</dd>\n </div>\n }\n </dl>\n </div>\n </section>\n </main>\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rblabs-dev/axiom",
3
- "version": "1.2.4",
3
+ "version": "1.3.0",
4
4
  "description": "Axiom, the RB Labs design system for Angular and Angular Material 3",
5
5
  "keywords": [
6
6
  "angular",
@@ -484,6 +484,11 @@
484
484
  letter-spacing: var(--rb-type-label-small-tracking);
485
485
  text-transform: uppercase;
486
486
  }
487
+
488
+ // Over a media stage (13.1): text-subtle is a theme color and too faint on the stage in light.
489
+ #{$r} .rb-overline--on-stage {
490
+ color: var(--rb-color-on-stage);
491
+ }
487
492
  }
488
493
 
489
494
  // Landing pages (15.13): the hero, tag lists and icon tiles of the public home pages.
@@ -27,7 +27,7 @@
27
27
  @forward 'breakpoints' show below, above, $breakpoints;
28
28
 
29
29
  // Stamped from projects/axiom/package.json by the build (scripts/stamp-version.mjs).
30
- $version: '1.2.4';
30
+ $version: '1.3.0';
31
31
 
32
32
  @mixin design-system($scope: null, $icon-font-url: null) {
33
33
  @include fonts.inter();
@@ -56,6 +56,10 @@ export declare class DesignSystemShowcaseComponent {
56
56
  overline: string;
57
57
  label: string;
58
58
  }[];
59
+ protected readonly stageMetrics: {
60
+ label: string;
61
+ value: string;
62
+ }[];
59
63
  /** `?example=<kind>` renders one shell example full page instead of the reference. */
60
64
  protected readonly example: import("@angular/core").Signal<RbShellExampleKind | null>;
61
65
  private readonly sanitizer;