{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "banner",
  "title": "Banner",
  "description": "Persistent in-flow application banner with semantic color intent, explicit announcement urgency, horizontal actions, and controlled dismissal motion.",
  "type": "registry:ui",
  "meta": {
    "library": "@ds-mo/ui",
    "distribution": "npm",
    "storybook": "https://zainadeel.github.io/compomo/?path=/story/banner",
    "source": "src/wc/components/Banner/Banner.tsx",
    "intentStatus": "complete",
    "intent": {
      "audience": "general",
      "status": "stable",
      "summary": "Persistent in-flow application banner with semantic color intent, explicit announcement urgency, horizontal actions, and controlled dismissal motion.",
      "useWhen": [
        "A persistent application-wide condition or announcement must remain visible while the user works.",
        "A shell-level message needs optional actions and dismissal while reflowing the application rather than floating over it.",
        "Message urgency and color intent must be selected independently."
      ],
      "avoidWhen": [
        "Feedback is transient, queued, promise-driven, or anchored to a control; use Toast.",
        "The message belongs inside a field, card, or bounded content section; use the future inline banner rather than shrinking the application shell.",
        "Several global messages must be prioritized or queued; the application must resolve them to one active banner before rendering."
      ],
      "alternatives": [
        {
          "when": "Feedback is transient or follows a completed action.",
          "component": "component:ds-toast",
          "reason": "Toast owns queues, timers, stacking, swipe dismissal, and anchored feedback."
        },
        {
          "when": "A bounded region has no content to show.",
          "component": "component:ds-empty-state",
          "reason": "EmptyState explains an empty result without creating global application urgency."
        }
      ],
      "commonlyComposedWith": [
        "component:ds-shell-app",
        "component:ds-button-unfilled"
      ],
      "patterns": [
        "pattern:application-shell"
      ],
      "accessibility": [
        "Choose announcement=none for content present at page load, polite for non-urgent dynamic changes, and assertive only when immediate interruption is necessary.",
        "Visual intent never chooses live-region urgency; a negative banner can be persistent and non-urgent, while a neutral banner can require prompt attention.",
        "The close control is always available. Localize dismissLabel and keep the banner mounted until dsAfterClose before removing it.",
        "Slotted actions retain their own accessible names, keyboard behavior, and outcome messaging."
      ],
      "states": [
        "The application owns open. The always-present close control emits dsClose, and the application sets open=false to begin exit motion.",
        "dsAfterClose fires once after the closing banner is inert, hidden from assistive technology, and no longer occupies layout space.",
        "The opaque surface slides by its complete block size while occupied-shell space expands with the same short-3 duration. Opening uses the shell tool panel's ease-out curve; dismissal reverses with its ease-in curve, keeping the Banner boundary flush with the shell throughout.",
        "Heading is optional and description is required. With orientation=horizontal they share one inline copy flow, so description begins after the heading and continuation lines use the complete text width beneath it. With orientation=vertical, description starts on the next line with a 4px gap.",
        "The actions slot accepts at most two ds-button-unfilled controls. They stay in one horizontal trailing lane, align to the top of the dismiss control when sharing the copy row, and must receive the matching parent-surface background context.",
        "Intent selects neutral, brand, positive, warning, caution, or negative hue. Contrast independently selects faint, medium, strong, or bold fill.",
        "Banner has a real bottom boundary, no radius, no elevation, no icon, no timeout, and no internal queue."
      ],
      "responsiveBehavior": [
        "Applications select orientation explicitly. Horizontal keeps copy and actions in one row, with actions top-aligned to dismissal. Vertical puts actions below copy on the 16px text boundary and keeps controls 16px above the bottom boundary; without actions it reserves no trailing row and keeps the copy visually balanced between the top and bottom edges.",
        "When placed in ShellApp's banner slot, the banner spans the complete application above navigation, workspace, and tools.",
        "ShellApp reserves the mobile top safe area once above the banner and keeps the banner visible while mobile tools or navigation surfaces replace the routed stage."
      ],
      "frameworkCaveats": {
        "customElements": [
          "Listen for dsClose, set open=false, and wait for dsAfterClose before conditionally removing the element.",
          "Place at most one ds-banner in ShellApp's banner slot, choose horizontal or vertical orientation for the available shell capacity, and assign no more than two ds-button-unfilled elements slot=actions."
        ],
        "react": [
          "Keep the component mounted while open changes from true to false; remove it in onDsAfterClose when unmounting is required.",
          "Project no more than two ds-button-unfilled actions and pass each the matching background context."
        ],
        "angular": [
          "Bind open as controlled application state, handle dsClose to update it, and wait for dsAfterClose before removing an ngIf owner.",
          "Import and project no more than two DsButtonUnfilled actions with slot=actions and their matching background input."
        ]
      },
      "references": [
        {
          "label": "Storybook examples",
          "path": "src/wc/components/Banner/Banner.stories.ts"
        },
        {
          "label": "Rendered behavior coverage",
          "path": "tests/e2e/banner.spec.ts"
        },
        {
          "label": "Application shell contract",
          "path": "agent/patterns/application-shell/pattern.agent.json"
        }
      ]
    },
    "api": {
      "props": {
        "announcement": {
          "type": "BannerAnnouncement",
          "resolvedType": "\"assertive\" | \"none\" | \"polite\"",
          "attribute": "announcement",
          "default": "'none'",
          "required": false,
          "mutable": false,
          "description": "Live-region urgency, independent of visual intent."
        },
        "contrast": {
          "type": "BannerContrast",
          "resolvedType": "\"bold\" | \"faint\" | \"medium\" | \"strong\"",
          "attribute": "contrast",
          "default": "'faint'",
          "required": false,
          "mutable": false,
          "description": "Surface fill weight."
        },
        "description": {
          "type": "string",
          "resolvedType": "string",
          "attribute": "description",
          "required": true,
          "mutable": false,
          "description": "Required explanatory copy."
        },
        "dismissLabel": {
          "type": "string",
          "resolvedType": "string",
          "attribute": "dismiss-label",
          "default": "'Dismiss banner'",
          "required": false,
          "mutable": false,
          "description": "Localized accessible label for the close control."
        },
        "heading": {
          "type": "string",
          "resolvedType": "string",
          "attribute": "heading",
          "default": "''",
          "required": false,
          "mutable": false,
          "description": "Optional visual heading rendered before the description."
        },
        "intent": {
          "type": "BannerIntent",
          "resolvedType": "\"brand\" | \"caution\" | \"negative\" | \"neutral\" | \"positive\" | \"warning\"",
          "attribute": "intent",
          "default": "'neutral'",
          "required": false,
          "mutable": false,
          "description": "Semantic color family."
        },
        "open": {
          "type": "boolean",
          "resolvedType": "boolean",
          "attribute": "open",
          "default": "true",
          "required": false,
          "mutable": false,
          "description": "Controlled visibility. Keep the element mounted until dsAfterClose when unmounting."
        },
        "orientation": {
          "type": "BannerOrientation",
          "resolvedType": "\"horizontal\" | \"vertical\"",
          "attribute": "orientation",
          "default": "'horizontal'",
          "required": false,
          "mutable": false,
          "description": "Copy and action-lane arrangement, selected by the application."
        }
      },
      "events": [
        {
          "name": "dsAfterClose",
          "detail": "void",
          "bubbles": true,
          "cancelable": true,
          "composed": true,
          "description": "Emitted once after controlled exit motion and layout collapse complete."
        },
        {
          "name": "dsClose",
          "detail": "MouseEvent",
          "bubbles": true,
          "cancelable": true,
          "composed": true,
          "description": "Emitted when the close control requests dismissal. The application controls open."
        }
      ],
      "methods": [],
      "slots": []
    },
    "props": {
      "announcement": {
        "type": "BannerAnnouncement",
        "resolvedType": "\"assertive\" | \"none\" | \"polite\"",
        "attribute": "announcement",
        "default": "'none'",
        "required": false,
        "mutable": false,
        "description": "Live-region urgency, independent of visual intent."
      },
      "contrast": {
        "type": "BannerContrast",
        "resolvedType": "\"bold\" | \"faint\" | \"medium\" | \"strong\"",
        "attribute": "contrast",
        "default": "'faint'",
        "required": false,
        "mutable": false,
        "description": "Surface fill weight."
      },
      "description": {
        "type": "string",
        "resolvedType": "string",
        "attribute": "description",
        "required": true,
        "mutable": false,
        "description": "Required explanatory copy."
      },
      "dismissLabel": {
        "type": "string",
        "resolvedType": "string",
        "attribute": "dismiss-label",
        "default": "'Dismiss banner'",
        "required": false,
        "mutable": false,
        "description": "Localized accessible label for the close control."
      },
      "heading": {
        "type": "string",
        "resolvedType": "string",
        "attribute": "heading",
        "default": "''",
        "required": false,
        "mutable": false,
        "description": "Optional visual heading rendered before the description."
      },
      "intent": {
        "type": "BannerIntent",
        "resolvedType": "\"brand\" | \"caution\" | \"negative\" | \"neutral\" | \"positive\" | \"warning\"",
        "attribute": "intent",
        "default": "'neutral'",
        "required": false,
        "mutable": false,
        "description": "Semantic color family."
      },
      "open": {
        "type": "boolean",
        "resolvedType": "boolean",
        "attribute": "open",
        "default": "true",
        "required": false,
        "mutable": false,
        "description": "Controlled visibility. Keep the element mounted until dsAfterClose when unmounting."
      },
      "orientation": {
        "type": "BannerOrientation",
        "resolvedType": "\"horizontal\" | \"vertical\"",
        "attribute": "orientation",
        "default": "'horizontal'",
        "required": false,
        "mutable": false,
        "description": "Copy and action-lane arrangement, selected by the application."
      }
    },
    "events": [
      {
        "name": "dsAfterClose",
        "detail": "void",
        "bubbles": true,
        "cancelable": true,
        "composed": true,
        "description": "Emitted once after controlled exit motion and layout collapse complete."
      },
      {
        "name": "dsClose",
        "detail": "MouseEvent",
        "bubbles": true,
        "cancelable": true,
        "composed": true,
        "description": "Emitted when the close control requests dismissal. The application controls open."
      }
    ],
    "methods": [],
    "slots": [],
    "exports": {
      "customElement": "ds-banner",
      "react": "DsBanner",
      "vue": "DsBanner",
      "angular": "DsBanner"
    },
    "consumption": {
      "install": "npm install @ds-mo/ui @ds-mo/tokens @ds-mo/icons",
      "cssSetup": "import '@ds-mo/tokens';\nimport '@ds-mo/tokens/reset';\nimport '@ds-mo/tokens/globals';",
      "customElements": {
        "import": "import '@ds-mo/ui/dist/components/ds-banner.js';",
        "example": "<ds-banner></ds-banner>"
      },
      "react": {
        "import": "import { DsBanner } from '@ds-mo/ui/react';",
        "example": "<DsBanner />"
      },
      "vue": {
        "import": "import { DsBanner } from '@ds-mo/ui/vue';",
        "example": "<DsBanner />"
      },
      "angular": {
        "import": "import { DsBanner } from '@ds-mo/ui/angular/ds-banner';",
        "example": "<ds-banner></ds-banner>"
      },
      "peerDependencies": {
        "required": [
          "@ds-mo/tokens ^6.5.0",
          "@ds-mo/icons ^7.0.0"
        ],
        "frameworks": "Custom Elements; React 18/19 wrappers; Vue 3 wrappers; Angular 19-22 standalone adapters."
      }
    }
  },
  "dependencies": [
    "@ds-mo/ui",
    "@ds-mo/tokens"
  ],
  "registryDependencies": [
    "button-unfilled",
    "text",
    "tooltip"
  ],
  "files": [
    {
      "path": "src/wc/components/Banner/Banner.css",
      "content": "@import '../../utils/forced-colors.css';\n@import '../../utils/chrome-header.css';\n\n:host {\n  display: grid;\n  overflow: hidden;\n  min-width: 0;\n  grid-template-rows: 0fr;\n  transition: grid-template-rows var(--effect-animation-duration-short-3)\n    var(--_banner-motion-easing);\n\n  --ds-banner-background: var(--color-background-faint-neutral);\n  --ds-banner-heading: var(--color-foreground-primary);\n  --ds-banner-description: var(--color-foreground-secondary);\n  --ds-banner-border: var(--color-border-tertiary);\n  --_banner-motion-easing: var(--effect-animation-easing-ease-in-out, ease);\n}\n\n:host(.banner--open) {\n  grid-template-rows: 1fr;\n  --_banner-motion-easing: var(--effect-animation-easing-ease-out, ease-out);\n}\n\n:host(.banner--closing) {\n  --_banner-motion-easing: var(--effect-animation-easing-ease-in, ease-in);\n}\n\n:host(.banner--hidden) {\n  display: none;\n}\n\n.banner-overflow {\n  min-height: 0;\n  overflow: visible;\n}\n\n.banner-surface {\n  display: flex;\n  position: relative;\n  width: 100%;\n  padding-inline-end: calc(var(--dimension-size-400) + var(--dimension-space-200));\n  color: var(--ds-banner-description);\n  background-color: var(--ds-banner-background);\n  border-bottom: var(--dimension-stroke-width-012) solid var(--ds-banner-border);\n  transform: translateY(-100%);\n  transition: transform var(--effect-animation-duration-short-3) var(--_banner-motion-easing);\n}\n\n:host(.banner--open) .banner-surface {\n  transform: translateY(0);\n}\n\n.banner-copy {\n  flex-basis: var(--dimension-card-width-sm);\n}\n\n:host(.banner--orientation-horizontal) .banner-surface {\n  flex-wrap: nowrap;\n}\n\n:host(.banner--orientation-vertical) .banner-copy {\n  flex-basis: 100%;\n}\n\n.banner-copy-flow {\n  box-sizing: border-box;\n  min-width: 0;\n  padding-inline: var(--ds-control-label-inset, var(--dimension-space-025));\n  color: var(--ds-banner-description);\n}\n\n.banner-heading {\n  display: inline-block;\n  width: auto;\n  max-width: 100%;\n  margin-inline-end: var(--dimension-space-100);\n  vertical-align: baseline;\n  color: var(--ds-banner-heading);\n}\n\n.banner-description {\n  color: var(--ds-banner-description);\n}\n\n.banner-actions {\n  display: flex;\n  align-items: center;\n  flex: 0 0 auto;\n  flex-wrap: nowrap;\n  min-width: 0;\n  gap: var(--dimension-space-100);\n}\n\n.banner-actions slot {\n  display: contents;\n}\n\n.banner-actions ::slotted(:not(ds-button-unfilled)),\n.banner-actions ::slotted(ds-button-unfilled:nth-of-type(n + 3)) {\n  display: none;\n}\n\n.banner-actions--empty {\n  display: none;\n}\n\n.banner-trailing {\n  align-self: flex-start;\n  flex-wrap: nowrap;\n}\n\n:host(.banner--orientation-vertical:not(.banner--has-actions)) .banner-trailing {\n  display: contents;\n}\n\n:host(.banner--orientation-vertical.banner--has-actions) .banner-trailing {\n  margin-inline: var(--dimension-space-100) auto;\n  padding-block-end: var(--dimension-space-100);\n  box-sizing: border-box;\n}\n\n:host(.banner--orientation-vertical) .banner-description {\n  display: block;\n}\n\n:host(.banner--orientation-vertical) .banner-heading + .banner-description {\n  margin-block-start: var(--dimension-space-050);\n}\n\n.banner-close {\n  flex: 0 0 auto;\n}\n\n.banner-dismiss {\n  position: absolute;\n  inset-block-start: var(--dimension-space-100);\n  inset-inline-end: var(--dimension-space-100);\n}\n\n:host(.banner--intent-neutral) {\n  --ds-banner-faint-background: var(--color-background-faint-neutral);\n}\n\n:host(.banner--intent-brand) {\n  --ds-banner-faint-background: var(--color-background-faint-brand);\n}\n\n:host(.banner--intent-positive) {\n  --ds-banner-faint-background: var(--color-background-faint-positive);\n}\n\n:host(.banner--intent-warning) {\n  --ds-banner-faint-background: var(--color-background-faint-warning);\n}\n\n:host(.banner--intent-caution) {\n  --ds-banner-faint-background: var(--color-background-faint-caution);\n}\n\n:host(.banner--intent-negative) {\n  --ds-banner-faint-background: var(--color-background-faint-negative);\n}\n\n:host(.banner--contrast-faint) {\n  --ds-banner-background: var(--ds-banner-faint-background);\n  --ds-banner-heading: var(--color-foreground-primary);\n  --ds-banner-description: var(--color-foreground-secondary);\n  --ds-banner-border: var(--color-border-tertiary);\n}\n\n:host(.banner--contrast-medium) {\n  --ds-banner-background: var(--color-background-medium-neutral);\n  --ds-banner-heading: var(--color-foreground-on-medium-background-primary);\n  --ds-banner-description: var(--color-foreground-on-medium-background-secondary);\n  --ds-banner-border: var(--color-border-on-medium-background-tertiary);\n}\n\n:host(.banner--intent-brand.banner--contrast-medium) { --ds-banner-background: var(--color-background-medium-brand); }\n:host(.banner--intent-positive.banner--contrast-medium) { --ds-banner-background: var(--color-background-medium-positive); }\n:host(.banner--intent-warning.banner--contrast-medium) { --ds-banner-background: var(--color-background-medium-warning); }\n:host(.banner--intent-caution.banner--contrast-medium) { --ds-banner-background: var(--color-background-medium-caution); }\n:host(.banner--intent-negative.banner--contrast-medium) { --ds-banner-background: var(--color-background-medium-negative); }\n\n:host(.banner--contrast-strong) {\n  --ds-banner-background: var(--color-background-strong-neutral);\n  --ds-banner-heading: var(--color-foreground-on-strong-background-primary);\n  --ds-banner-description: var(--color-foreground-on-strong-background-secondary);\n  --ds-banner-border: var(--color-border-on-strong-background-tertiary);\n}\n\n:host(.banner--intent-brand.banner--contrast-strong) { --ds-banner-background: var(--color-background-strong-brand); }\n:host(.banner--intent-positive.banner--contrast-strong) { --ds-banner-background: var(--color-background-strong-positive); }\n:host(.banner--intent-warning.banner--contrast-strong) { --ds-banner-background: var(--color-background-strong-warning); }\n:host(.banner--intent-caution.banner--contrast-strong) { --ds-banner-background: var(--color-background-strong-caution); }\n:host(.banner--intent-negative.banner--contrast-strong) { --ds-banner-background: var(--color-background-strong-negative); }\n\n:host(.banner--contrast-bold) {\n  --ds-banner-background: var(--color-background-bold-neutral);\n  --ds-banner-heading: var(--color-foreground-on-bold-background-primary);\n  --ds-banner-description: var(--color-foreground-on-bold-background-secondary);\n  --ds-banner-border: var(--color-border-on-bold-background-tertiary);\n}\n\n:host(.banner--intent-brand.banner--contrast-bold) { --ds-banner-background: var(--color-background-bold-brand); }\n:host(.banner--intent-positive.banner--contrast-bold) { --ds-banner-background: var(--color-background-bold-positive); }\n:host(.banner--intent-warning.banner--contrast-bold) { --ds-banner-background: var(--color-background-bold-warning); }\n:host(.banner--intent-caution.banner--contrast-bold) { --ds-banner-background: var(--color-background-bold-caution); }\n:host(.banner--intent-negative.banner--contrast-bold) { --ds-banner-background: var(--color-background-bold-negative); }\n\n@media (prefers-reduced-motion: reduce) {\n  :host,\n  .banner-surface {\n    transition: none;\n  }\n}\n\n@media (forced-colors: active) {\n  .banner-surface {\n    color: var(--ds-forced-color-content);\n    background-color: var(--ds-forced-color-surface);\n    border-bottom-color: var(--ds-forced-color-content);\n  }\n\n  .banner-heading,\n  .banner-description {\n    color: var(--ds-forced-color-content);\n  }\n}\n",
      "type": "registry:ui"
    },
    {
      "path": "src/wc/components/Banner/Banner.tsx",
      "content": "import {\n  Component,\n  Element,\n  Event,\n  EventEmitter,\n  h,\n  Host,\n  Prop,\n  State,\n  Watch,\n} from '@stencil/core';\nimport { resolveMotionTimeMs, TOKEN_DEFAULTS } from '../../utils';\n\nexport type BannerIntent =\n  | 'neutral'\n  | 'brand'\n  | 'positive'\n  | 'warning'\n  | 'caution'\n  | 'negative';\n\nexport type BannerContrast = 'faint' | 'medium' | 'strong' | 'bold';\nexport type BannerAnnouncement = 'none' | 'polite' | 'assertive';\nexport type BannerOrientation = 'horizontal' | 'vertical';\n\ntype BannerPhase = 'closed' | 'open' | 'closing';\n\n@Component({\n  tag: 'ds-banner',\n  styleUrl: 'Banner.css',\n  scoped: true,\n})\nexport class Banner {\n  @Element() el!: HTMLElement;\n\n  /** Required explanatory copy. */\n  @Prop() description!: string;\n  /** Optional visual heading rendered before the description. */\n  @Prop() heading: string = '';\n  /** Semantic color family. */\n  @Prop({ reflect: true }) intent: BannerIntent = 'neutral';\n  /** Surface fill weight. */\n  @Prop({ reflect: true }) contrast: BannerContrast = 'faint';\n  /** Copy and action-lane arrangement, selected by the application. */\n  @Prop({ reflect: true }) orientation: BannerOrientation = 'horizontal';\n  /** Controlled visibility. Keep the element mounted until dsAfterClose when unmounting. */\n  @Prop({ reflect: true }) open: boolean = true;\n  /** Localized accessible label for the close control. */\n  @Prop() dismissLabel: string = 'Dismiss banner';\n  /** Live-region urgency, independent of visual intent. */\n  @Prop() announcement: BannerAnnouncement = 'none';\n\n  /** Emitted when the close control requests dismissal. The application controls open. */\n  @Event() dsClose!: EventEmitter<MouseEvent>;\n  /** Emitted once after controlled exit motion and layout collapse complete. */\n  @Event() dsAfterClose!: EventEmitter<void>;\n\n  @State() private rendered = false;\n  @State() private phase: BannerPhase = 'closed';\n  @State() private hasActions = false;\n\n  private loaded = false;\n  private transitionTimer: ReturnType<typeof setTimeout> | null = null;\n  private openFrame: number | null = null;\n  private closePending = false;\n\n  componentWillLoad() {\n    this.rendered = this.open;\n  }\n\n  componentDidLoad() {\n    this.loaded = true;\n    this.updateActionsPresence();\n    if (this.open) this.startOpen();\n  }\n\n  disconnectedCallback() {\n    this.clearTransitionWork();\n  }\n\n  @Watch('open')\n  onOpenChange(isOpen: boolean) {\n    if (!this.loaded) return;\n    if (isOpen) this.startOpen();\n    else this.startClose();\n  }\n\n  private get closeMotionMs(): number {\n    return resolveMotionTimeMs(\n      TOKEN_DEFAULTS.motionShort3,\n      TOKEN_DEFAULTS.animationDurationShort3,\n    );\n  }\n\n  private clearTransitionWork() {\n    if (this.transitionTimer) clearTimeout(this.transitionTimer);\n    if (this.openFrame !== null) cancelAnimationFrame(this.openFrame);\n    this.transitionTimer = null;\n    this.openFrame = null;\n  }\n\n  private startOpen() {\n    this.clearTransitionWork();\n    this.closePending = false;\n    this.rendered = true;\n    this.phase = 'closed';\n    this.openFrame = requestAnimationFrame(() => {\n      this.openFrame = null;\n      if (this.open) this.phase = 'open';\n    });\n  }\n\n  private startClose() {\n    this.clearTransitionWork();\n    if (!this.rendered || this.phase === 'closed') {\n      this.rendered = false;\n      return;\n    }\n\n    this.closePending = true;\n    this.phase = 'closing';\n    const duration = this.closeMotionMs;\n    if (duration <= 0) {\n      this.finishClose();\n      return;\n    }\n    this.transitionTimer = setTimeout(() => {\n      this.transitionTimer = null;\n      this.finishClose();\n    }, duration);\n  }\n\n  private finishClose() {\n    if (this.open || !this.closePending) return;\n    this.closePending = false;\n    this.rendered = false;\n    this.phase = 'closed';\n    this.dsAfterClose.emit();\n  }\n\n  private updateActionsPresence(slot?: HTMLSlotElement) {\n    const actions = slot\n      ? slot.assignedElements().filter(element => element.tagName === 'DS-BUTTON-UNFILLED')\n      : Array.from(this.el.querySelectorAll('ds-button-unfilled[slot=\"actions\"]'));\n    this.hasActions = actions.length > 0;\n  }\n\n  private requestClose(event: CustomEvent<MouseEvent>) {\n    if (!this.open || this.phase === 'closing') return;\n    this.dsClose.emit(event.detail);\n  }\n\n  private get role(): 'status' | 'alert' | undefined {\n    if (this.announcement === 'polite') return 'status';\n    if (this.announcement === 'assertive') return 'alert';\n    return undefined;\n  }\n\n  private get ariaLive(): 'polite' | 'assertive' | undefined {\n    return this.announcement === 'none' ? undefined : this.announcement;\n  }\n\n  render() {\n    const closing = this.phase === 'closing';\n    const hostClass = {\n      banner: true,\n      'banner--open': this.phase === 'open',\n      'banner--closing': closing,\n      'banner--hidden': !this.rendered,\n      'banner--has-actions': this.hasActions,\n      [`banner--orientation-${this.orientation}`]: true,\n      [`banner--intent-${this.intent}`]: true,\n      [`banner--contrast-${this.contrast}`]: true,\n    };\n\n    return (\n      <Host class={hostClass}>\n        {this.rendered && (\n          <div class=\"banner-overflow\">\n            <section\n              class=\"banner-surface ds-chrome-header ds-chrome-header--wrapping\"\n              role={this.role}\n              aria-live={this.ariaLive}\n              aria-atomic={this.announcement === 'none' ? undefined : 'true'}\n              aria-hidden={closing ? 'true' : undefined}\n              inert={closing ? true : undefined}\n              data-phase={this.phase}\n            >\n              {/* eslint-disable-next-line local/prefer-direct-ds-text -- The header-copy lane owns the 6px container inset separately from the inline text flow's 2px inset. */}\n              <div class=\"banner-copy ds-chrome-header__copy ds-chrome-header__copy--wrapping ds-control--md\">\n                <ds-text\n                  class=\"banner-copy-flow\"\n                  as=\"div\"\n                  variant=\"text-body-medium\"\n                  color=\"inherit\"\n                >\n                  {this.heading && (\n                    <ds-text\n                      class=\"banner-heading\"\n                      as=\"span\"\n                      variant=\"text-title-small\"\n                      color=\"inherit\"\n                    >\n                      {this.heading}\n                    </ds-text>\n                  )}\n                  <span class=\"banner-description\">{this.description}</span>\n                </ds-text>\n              </div>\n              <div class=\"banner-trailing ds-chrome-header__trailing\">\n                <div class={{ 'banner-actions': true, 'banner-actions--empty': !this.hasActions }}>\n                  <slot\n                    name=\"actions\"\n                    onSlotchange={(event: Event) =>\n                      this.updateActionsPresence(event.currentTarget as HTMLSlotElement)\n                    }\n                  />\n                </div>\n                <div class=\"banner-dismiss\">\n                  <ds-tooltip label={this.dismissLabel} side=\"bottom\" size=\"sm\">\n                    <ds-button-unfilled\n                      class=\"banner-close\"\n                      variant=\"icon\"\n                      icon=\"Cross\"\n                      size=\"md\"\n                      type=\"button\"\n                      background={this.contrast}\n                      aria-label={this.dismissLabel}\n                      hasBorder={false}\n                      activeFill={false}\n                      onDsClick={(event: CustomEvent<MouseEvent>) => this.requestClose(event)}\n                    />\n                  </ds-tooltip>\n                </div>\n              </div>\n            </section>\n          </div>\n        )}\n      </Host>\n    );\n  }\n}\n",
      "type": "registry:ui"
    }
  ]
}
