md-snackbar spec card
Identity · when to use / when NOT · decision cues · behavioural contract · do/don't · anti-patterns · full API. Paste into your agent when you're implementing with this component.
Brief, low-priority feedback about something that just happened. It appears over the content, auto-hides after 4 seconds, and can carry a single action — canonically “Undo”.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-button variant="filled">Show snackbar</md-button>
<md-snackbar id="snack-preview" message="Message archived" action="Undo" closeable></md-snackbar>
<script type="module">
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-preview').show());
</script>import { useRef } from 'react';
import { MdButton, MdSnackbar } from '@awc-ui/react';
export function Demo() {
const snackPreviewRef = useRef(null);
return (
<>
<MdButton variant="filled" onClick={() => snackPreviewRef.current?.show()}>Show snackbar</MdButton>
<MdSnackbar id="snack-preview" ref={snackPreviewRef} message="Message archived" action="Undo" closeable></MdSnackbar>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-button variant="filled" (click)="snackPreview.show()">Show snackbar</md-button>
<md-snackbar id="snack-preview" #snackPreview message="Message archived" action="Undo" closeable></md-snackbar><script setup>
import { ref } from 'vue';
import '@awc-ui/core/define';
const snackPreview = ref(null);
</script>
<template>
<md-button variant="filled" @click="snackPreview?.show()">Show snackbar</md-button>
<md-snackbar id="snack-preview" ref="snackPreview" message="Message archived" action="Undo" closeable></md-snackbar>
</template><script>
import '@awc-ui/core/define';
let snackPreview;
</script>
<md-button variant="filled" on:click={() => snackPreview?.show()}>Show snackbar</md-button>
<md-snackbar id="snack-preview" bind:this={snackPreview} message="Message archived" action="Undo" closeable></md-snackbar>
Already installed? See the
Installation guide for one-time package setup
(core + tokens, fonts). Each tab below shows two patterns for using
md-snackbar in your project: Option A registers every AWC UI
component at once (simplest), Option B imports only this component for
tree-shake-friendly bundles.
<!-- ─── Option A: global registration (all components) ─── -->
<script type="module">
import '@awc-ui/core/define';
</script>
<!-- ─── Option B: single import (tree-shake only md-snackbar) ─── -->
<script type="module">
import '@awc-ui/core/components/md-snackbar';
</script>
<md-snackbar></md-snackbar>// ─── Option A: typed React wrapper (registers all components) ───
// Importing from '@awc-ui/react' calls defineCustomElements() as a
// side effect, so every md-* element becomes available in the browser.
import { MdSnackbar } from '@awc-ui/react';
export function Example() {
return <MdSnackbar></MdSnackbar>;
}
// ─── Option B: single import (tree-shake to only md-snackbar) ───
// Skip the wrapper and use the raw custom element. Smallest bundle,
// but you lose typed props/events on JSX.
import '@awc-ui/core/components/md-snackbar';
export function ExampleTreeShaken() {
return <md-snackbar></md-snackbar>;
}// ─── Option A: schema module (any md-* element accepted) ───
// Pair with `defineCustomElements(window)` in main.ts.
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@Component({
standalone: true,
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `<md-snackbar></md-snackbar>`,
})
export class ExampleComponent {}
// ─── Option B: typed directive (tree-shake friendly) ───
// Pair with `import '@awc-ui/core/components/md-snackbar'` in main.ts.
import { Component } from '@angular/core';
import { MdSnackbar } from '@awc-ui/angular';
@Component({
standalone: true,
imports: [MdSnackbar],
template: `<md-snackbar></md-snackbar>`,
})
export class ExampleTreeShakenComponent {}<!-- ─── Option A: typed Vue wrapper (registers all components) ─── -->
<script setup lang="ts">
import { MdSnackbar } from '@awc-ui/vue';
</script>
<template>
<MdSnackbar></MdSnackbar>
</template>
<!-- ─── Option B: single import (tree-shake to only md-snackbar) ─── -->
<script setup lang="ts">
import '@awc-ui/core/components/md-snackbar';
</script>
<template>
<md-snackbar></md-snackbar>
</template><!-- ─── Option A: global registration (done once in main entry) ─── -->
<!-- main.ts: -->
<!-- import { defineCustomElements } from '@awc-ui/svelte'; -->
<!-- defineCustomElements(window); -->
<md-snackbar></md-snackbar>
<!-- ─── Option B: single import (tree-shake to only md-snackbar) ─── -->
<script lang="ts">
import '@awc-ui/core/components/md-snackbar';
</script>
<md-snackbar></md-snackbar>| Situation | Use instead |
|---|---|
| A decision that must block the flow | md-dialog |
| Critical errors the user must acknowledge | md-dialog |
| Explaining a control | md-tooltip |
| Persistent status | Inline text, md-chip, md-badge |
| A message needing an icon | md-dialog — M3 says avoid icons in snackbars |
| A message needing a link | Add a button instead, or a different component |
| Several messages at once | Queue them — never stack or place them side by side |
| Form validation | Inline field errors |
message is the label. action renders a text button — M3 forbids filled
or elevated buttons here, so don’t slot one in. Keep the label to one line where
possible (two on mobile).
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-button variant="filled">Single line</md-button>
<md-button variant="filled">Single line + action</md-button>
<md-button variant="filled">Two lines</md-button>
<md-button variant="filled">Two lines + action</md-button>
<md-snackbar id="snack-single" message="File saved successfully"></md-snackbar>
<md-snackbar id="snack-action" message="Item deleted" action="Undo"></md-snackbar>
<md-snackbar id="snack-two" message="Your export finished and is ready to download from the reports page"></md-snackbar>
<md-snackbar id="snack-two-action" message="Your export finished and is ready to download from the reports page" action="Undo"></md-snackbar>
<script type="module">
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-single').show());
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-action').show());
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-two').show());
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-two-action').show());
</script>import { useRef } from 'react';
import { MdButton, MdSnackbar } from '@awc-ui/react';
export function Demo() {
const snackSingleRef = useRef(null);
const snackActionRef = useRef(null);
const snackTwoRef = useRef(null);
const snackTwoActionRef = useRef(null);
return (
<>
<MdButton variant="filled" onClick={() => snackSingleRef.current?.show()}>Single line</MdButton>
<MdButton variant="filled" onClick={() => snackActionRef.current?.show()}>Single line + action</MdButton>
<MdButton variant="filled" onClick={() => snackTwoRef.current?.show()}>Two lines</MdButton>
<MdButton variant="filled" onClick={() => snackTwoActionRef.current?.show()}>Two lines + action</MdButton>
<MdSnackbar id="snack-single" ref={snackSingleRef} message="File saved successfully"></MdSnackbar>
<MdSnackbar id="snack-action" ref={snackActionRef} message="Item deleted" action="Undo"></MdSnackbar>
<MdSnackbar id="snack-two" ref={snackTwoRef} message="Your export finished and is ready to download from the reports page"></MdSnackbar>
<MdSnackbar id="snack-two-action" ref={snackTwoActionRef} message="Your export finished and is ready to download from the reports page" action="Undo"></MdSnackbar>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-button variant="filled" (click)="snackSingle.show()">Single line</md-button>
<md-button variant="filled" (click)="snackAction.show()">Single line + action</md-button>
<md-button variant="filled" (click)="snackTwo.show()">Two lines</md-button>
<md-button variant="filled" (click)="snackTwoAction.show()">Two lines + action</md-button>
<md-snackbar id="snack-single" #snackSingle message="File saved successfully"></md-snackbar>
<md-snackbar id="snack-action" #snackAction message="Item deleted" action="Undo"></md-snackbar>
<md-snackbar id="snack-two" #snackTwo message="Your export finished and is ready to download from the reports page"></md-snackbar>
<md-snackbar id="snack-two-action" #snackTwoAction message="Your export finished and is ready to download from the reports page" action="Undo"></md-snackbar><script setup>
import { ref } from 'vue';
import '@awc-ui/core/define';
const snackSingle = ref(null);
const snackAction = ref(null);
const snackTwo = ref(null);
const snackTwoAction = ref(null);
</script>
<template>
<md-button variant="filled" @click="snackSingle?.show()">Single line</md-button>
<md-button variant="filled" @click="snackAction?.show()">Single line + action</md-button>
<md-button variant="filled" @click="snackTwo?.show()">Two lines</md-button>
<md-button variant="filled" @click="snackTwoAction?.show()">Two lines + action</md-button>
<md-snackbar id="snack-single" ref="snackSingle" message="File saved successfully"></md-snackbar>
<md-snackbar id="snack-action" ref="snackAction" message="Item deleted" action="Undo"></md-snackbar>
<md-snackbar id="snack-two" ref="snackTwo" message="Your export finished and is ready to download from the reports page"></md-snackbar>
<md-snackbar id="snack-two-action" ref="snackTwoAction" message="Your export finished and is ready to download from the reports page" action="Undo"></md-snackbar>
</template><script>
import '@awc-ui/core/define';
let snackSingle;
let snackAction;
let snackTwo;
let snackTwoAction;
</script>
<md-button variant="filled" on:click={() => snackSingle?.show()}>Single line</md-button>
<md-button variant="filled" on:click={() => snackAction?.show()}>Single line + action</md-button>
<md-button variant="filled" on:click={() => snackTwo?.show()}>Two lines</md-button>
<md-button variant="filled" on:click={() => snackTwoAction?.show()}>Two lines + action</md-button>
<md-snackbar id="snack-single" bind:this={snackSingle} message="File saved successfully"></md-snackbar>
<md-snackbar id="snack-action" bind:this={snackAction} message="Item deleted" action="Undo"></md-snackbar>
<md-snackbar id="snack-two" bind:this={snackTwo} message="Your export finished and is ready to download from the reports page"></md-snackbar>
<md-snackbar id="snack-two-action" bind:this={snackTwoAction} message="Your export finished and is ready to download from the reports page" action="Undo"></md-snackbar>Each of the four is its own story: single line, single line + action, two lines, two lines + action.
stacked moves the action onto its own line. Use it when a long message and a
long action would otherwise crowd each other.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-button variant="filled">Stacked</md-button>
<md-snackbar id="snack-stacked" message="Item moved to trash and removed from all shared boards" action="Undo this move" closeable stacked></md-snackbar>
<script type="module">
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-stacked').show());
</script>import { useRef } from 'react';
import { MdButton, MdSnackbar } from '@awc-ui/react';
export function Demo() {
const snackStackedRef = useRef(null);
return (
<>
<MdButton variant="filled" onClick={() => snackStackedRef.current?.show()}>Stacked</MdButton>
<MdSnackbar id="snack-stacked" ref={snackStackedRef} message="Item moved to trash and removed from all shared boards" action="Undo this move" closeable stacked></MdSnackbar>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-button variant="filled" (click)="snackStacked.show()">Stacked</md-button>
<md-snackbar id="snack-stacked" #snackStacked message="Item moved to trash and removed from all shared boards" action="Undo this move" closeable stacked></md-snackbar><script setup>
import { ref } from 'vue';
import '@awc-ui/core/define';
const snackStacked = ref(null);
</script>
<template>
<md-button variant="filled" @click="snackStacked?.show()">Stacked</md-button>
<md-snackbar id="snack-stacked" ref="snackStacked" message="Item moved to trash and removed from all shared boards" action="Undo this move" closeable stacked></md-snackbar>
</template><script>
import '@awc-ui/core/define';
let snackStacked;
</script>
<md-button variant="filled" on:click={() => snackStacked?.show()}>Stacked</md-button>
<md-snackbar id="snack-stacked" bind:this={snackStacked} message="Item moved to trash and removed from all shared boards" action="Undo this move" closeable stacked></md-snackbar>Auto-hide is on by default at 4000ms. closeable adds an X icon button.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-button variant="filled">Closeable</md-button>
<md-button variant="filled">Action + close</md-button>
<md-button variant="filled">Longer duration (10s)</md-button>
<md-button variant="filled">Persistent</md-button>
<md-snackbar id="snack-close" message="Connection restored" closeable></md-snackbar>
<md-snackbar id="snack-action-close" message="Message archived" action="View" closeable></md-snackbar>
<md-snackbar id="snack-slow" message="Take your time reading this one" action="Undo" auto-hide-duration="10000" closeable></md-snackbar>
<md-snackbar id="snack-persist" message="No connection" action="Retry" auto-hide="false" closeable politeness="assertive" dismiss-label="Dismiss"></md-snackbar>
<script type="module">
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-close').show());
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-action-close').show());
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-slow').show());
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-persist').show());
</script>import { useRef } from 'react';
import { MdButton, MdSnackbar } from '@awc-ui/react';
export function Demo() {
const snackCloseRef = useRef(null);
const snackActionCloseRef = useRef(null);
const snackSlowRef = useRef(null);
const snackPersistRef = useRef(null);
return (
<>
<MdButton variant="filled" onClick={() => snackCloseRef.current?.show()}>Closeable</MdButton>
<MdButton variant="filled" onClick={() => snackActionCloseRef.current?.show()}>Action + close</MdButton>
<MdButton variant="filled" onClick={() => snackSlowRef.current?.show()}>Longer duration (10s)</MdButton>
<MdButton variant="filled" onClick={() => snackPersistRef.current?.show()}>Persistent</MdButton>
<MdSnackbar id="snack-close" ref={snackCloseRef} message="Connection restored" closeable></MdSnackbar>
<MdSnackbar id="snack-action-close" ref={snackActionCloseRef} message="Message archived" action="View" closeable></MdSnackbar>
<MdSnackbar id="snack-slow" ref={snackSlowRef} message="Take your time reading this one" action="Undo" autoHideDuration="10000" closeable></MdSnackbar>
<MdSnackbar id="snack-persist" ref={snackPersistRef} message="No connection" action="Retry" autoHide="false" closeable politeness="assertive" dismissLabel="Dismiss"></MdSnackbar>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-button variant="filled" (click)="snackClose.show()">Closeable</md-button>
<md-button variant="filled" (click)="snackActionClose.show()">Action + close</md-button>
<md-button variant="filled" (click)="snackSlow.show()">Longer duration (10s)</md-button>
<md-button variant="filled" (click)="snackPersist.show()">Persistent</md-button>
<md-snackbar id="snack-close" #snackClose message="Connection restored" closeable></md-snackbar>
<md-snackbar id="snack-action-close" #snackActionClose message="Message archived" action="View" closeable></md-snackbar>
<md-snackbar id="snack-slow" #snackSlow message="Take your time reading this one" action="Undo" auto-hide-duration="10000" closeable></md-snackbar>
<md-snackbar id="snack-persist" #snackPersist message="No connection" action="Retry" auto-hide="false" closeable politeness="assertive" dismiss-label="Dismiss"></md-snackbar><script setup>
import { ref } from 'vue';
import '@awc-ui/core/define';
const snackClose = ref(null);
const snackActionClose = ref(null);
const snackSlow = ref(null);
const snackPersist = ref(null);
</script>
<template>
<md-button variant="filled" @click="snackClose?.show()">Closeable</md-button>
<md-button variant="filled" @click="snackActionClose?.show()">Action + close</md-button>
<md-button variant="filled" @click="snackSlow?.show()">Longer duration (10s)</md-button>
<md-button variant="filled" @click="snackPersist?.show()">Persistent</md-button>
<md-snackbar id="snack-close" ref="snackClose" message="Connection restored" closeable></md-snackbar>
<md-snackbar id="snack-action-close" ref="snackActionClose" message="Message archived" action="View" closeable></md-snackbar>
<md-snackbar id="snack-slow" ref="snackSlow" message="Take your time reading this one" action="Undo" auto-hide-duration="10000" closeable></md-snackbar>
<md-snackbar id="snack-persist" ref="snackPersist" message="No connection" action="Retry" auto-hide="false" closeable politeness="assertive" dismiss-label="Dismiss"></md-snackbar>
</template><script>
import '@awc-ui/core/define';
let snackClose;
let snackActionClose;
let snackSlow;
let snackPersist;
</script>
<md-button variant="filled" on:click={() => snackClose?.show()}>Closeable</md-button>
<md-button variant="filled" on:click={() => snackActionClose?.show()}>Action + close</md-button>
<md-button variant="filled" on:click={() => snackSlow?.show()}>Longer duration (10s)</md-button>
<md-button variant="filled" on:click={() => snackPersist?.show()}>Persistent</md-button>
<md-snackbar id="snack-close" bind:this={snackClose} message="Connection restored" closeable></md-snackbar>
<md-snackbar id="snack-action-close" bind:this={snackActionClose} message="Message archived" action="View" closeable></md-snackbar>
<md-snackbar id="snack-slow" bind:this={snackSlow} message="Take your time reading this one" action="Undo" auto-hide-duration="10000" closeable></md-snackbar>
<md-snackbar id="snack-persist" bind:this={snackPersist} message="No connection" action="Retry" auto-hide="false" closeable politeness="assertive" dismiss-label="Dismiss"></md-snackbar>In Storybook: close icon only, action + close, and the non-dismissive case the callout above warns about.
position takes nine values. start and end are logical and mirror under
RTL. M3: don’t place a snackbar flush to one edge of the layout, in front of
navigation, or in front of / behind a FAB.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-button variant="outlined">top-start</md-button>
<md-button variant="outlined">top</md-button>
<md-button variant="outlined">top-end</md-button>
<md-button variant="outlined">start</md-button>
<md-button variant="outlined">center</md-button>
<md-button variant="outlined">end</md-button>
<md-button variant="outlined">bottom-start</md-button>
<md-button variant="filled">bottom</md-button>
<md-button variant="outlined">bottom-end</md-button>
<md-snackbar id="snack-ts" position="top-start" message="Top-start snackbar" closeable></md-snackbar>
<md-snackbar id="snack-t" position="top" message="Top snackbar" closeable></md-snackbar>
<md-snackbar id="snack-te" position="top-end" message="Top-end snackbar" closeable></md-snackbar>
<md-snackbar id="snack-s" position="start" message="Start snackbar" closeable></md-snackbar>
<md-snackbar id="snack-c" position="center" message="Center snackbar" closeable></md-snackbar>
<md-snackbar id="snack-e" position="end" message="End snackbar" closeable></md-snackbar>
<md-snackbar id="snack-bs" position="bottom-start" message="Bottom-start snackbar" closeable></md-snackbar>
<md-snackbar id="snack-b" position="bottom" message="Bottom snackbar (default)" closeable></md-snackbar>
<md-snackbar id="snack-be" position="bottom-end" message="Bottom-end snackbar" closeable></md-snackbar>
<script type="module">
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-ts').show());
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-t').show());
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-te').show());
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-s').show());
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-c').show());
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-e').show());
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-bs').show());
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-b').show());
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-be').show());
</script>import { useRef } from 'react';
import { MdButton, MdSnackbar } from '@awc-ui/react';
export function Demo() {
const snackTsRef = useRef(null);
const snackTRef = useRef(null);
const snackTeRef = useRef(null);
const snackSRef = useRef(null);
const snackCRef = useRef(null);
const snackERef = useRef(null);
const snackBsRef = useRef(null);
const snackBRef = useRef(null);
const snackBeRef = useRef(null);
return (
<>
<MdButton variant="outlined" onClick={() => snackTsRef.current?.show()}>top-start</MdButton>
<MdButton variant="outlined" onClick={() => snackTRef.current?.show()}>top</MdButton>
<MdButton variant="outlined" onClick={() => snackTeRef.current?.show()}>top-end</MdButton>
<MdButton variant="outlined" onClick={() => snackSRef.current?.show()}>start</MdButton>
<MdButton variant="outlined" onClick={() => snackCRef.current?.show()}>center</MdButton>
<MdButton variant="outlined" onClick={() => snackERef.current?.show()}>end</MdButton>
<MdButton variant="outlined" onClick={() => snackBsRef.current?.show()}>bottom-start</MdButton>
<MdButton variant="filled" onClick={() => snackBRef.current?.show()}>bottom</MdButton>
<MdButton variant="outlined" onClick={() => snackBeRef.current?.show()}>bottom-end</MdButton>
<MdSnackbar id="snack-ts" ref={snackTsRef} position="top-start" message="Top-start snackbar" closeable></MdSnackbar>
<MdSnackbar id="snack-t" ref={snackTRef} position="top" message="Top snackbar" closeable></MdSnackbar>
<MdSnackbar id="snack-te" ref={snackTeRef} position="top-end" message="Top-end snackbar" closeable></MdSnackbar>
<MdSnackbar id="snack-s" ref={snackSRef} position="start" message="Start snackbar" closeable></MdSnackbar>
<MdSnackbar id="snack-c" ref={snackCRef} position="center" message="Center snackbar" closeable></MdSnackbar>
<MdSnackbar id="snack-e" ref={snackERef} position="end" message="End snackbar" closeable></MdSnackbar>
<MdSnackbar id="snack-bs" ref={snackBsRef} position="bottom-start" message="Bottom-start snackbar" closeable></MdSnackbar>
<MdSnackbar id="snack-b" ref={snackBRef} position="bottom" message="Bottom snackbar (default)" closeable></MdSnackbar>
<MdSnackbar id="snack-be" ref={snackBeRef} position="bottom-end" message="Bottom-end snackbar" closeable></MdSnackbar>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-button variant="outlined" (click)="snackTs.show()">top-start</md-button>
<md-button variant="outlined" (click)="snackT.show()">top</md-button>
<md-button variant="outlined" (click)="snackTe.show()">top-end</md-button>
<md-button variant="outlined" (click)="snackS.show()">start</md-button>
<md-button variant="outlined" (click)="snackC.show()">center</md-button>
<md-button variant="outlined" (click)="snackE.show()">end</md-button>
<md-button variant="outlined" (click)="snackBs.show()">bottom-start</md-button>
<md-button variant="filled" (click)="snackB.show()">bottom</md-button>
<md-button variant="outlined" (click)="snackBe.show()">bottom-end</md-button>
<md-snackbar id="snack-ts" #snackTs position="top-start" message="Top-start snackbar" closeable></md-snackbar>
<md-snackbar id="snack-t" #snackT position="top" message="Top snackbar" closeable></md-snackbar>
<md-snackbar id="snack-te" #snackTe position="top-end" message="Top-end snackbar" closeable></md-snackbar>
<md-snackbar id="snack-s" #snackS position="start" message="Start snackbar" closeable></md-snackbar>
<md-snackbar id="snack-c" #snackC position="center" message="Center snackbar" closeable></md-snackbar>
<md-snackbar id="snack-e" #snackE position="end" message="End snackbar" closeable></md-snackbar>
<md-snackbar id="snack-bs" #snackBs position="bottom-start" message="Bottom-start snackbar" closeable></md-snackbar>
<md-snackbar id="snack-b" #snackB position="bottom" message="Bottom snackbar (default)" closeable></md-snackbar>
<md-snackbar id="snack-be" #snackBe position="bottom-end" message="Bottom-end snackbar" closeable></md-snackbar><script setup>
import { ref } from 'vue';
import '@awc-ui/core/define';
const snackTs = ref(null);
const snackT = ref(null);
const snackTe = ref(null);
const snackS = ref(null);
const snackC = ref(null);
const snackE = ref(null);
const snackBs = ref(null);
const snackB = ref(null);
const snackBe = ref(null);
</script>
<template>
<md-button variant="outlined" @click="snackTs?.show()">top-start</md-button>
<md-button variant="outlined" @click="snackT?.show()">top</md-button>
<md-button variant="outlined" @click="snackTe?.show()">top-end</md-button>
<md-button variant="outlined" @click="snackS?.show()">start</md-button>
<md-button variant="outlined" @click="snackC?.show()">center</md-button>
<md-button variant="outlined" @click="snackE?.show()">end</md-button>
<md-button variant="outlined" @click="snackBs?.show()">bottom-start</md-button>
<md-button variant="filled" @click="snackB?.show()">bottom</md-button>
<md-button variant="outlined" @click="snackBe?.show()">bottom-end</md-button>
<md-snackbar id="snack-ts" ref="snackTs" position="top-start" message="Top-start snackbar" closeable></md-snackbar>
<md-snackbar id="snack-t" ref="snackT" position="top" message="Top snackbar" closeable></md-snackbar>
<md-snackbar id="snack-te" ref="snackTe" position="top-end" message="Top-end snackbar" closeable></md-snackbar>
<md-snackbar id="snack-s" ref="snackS" position="start" message="Start snackbar" closeable></md-snackbar>
<md-snackbar id="snack-c" ref="snackC" position="center" message="Center snackbar" closeable></md-snackbar>
<md-snackbar id="snack-e" ref="snackE" position="end" message="End snackbar" closeable></md-snackbar>
<md-snackbar id="snack-bs" ref="snackBs" position="bottom-start" message="Bottom-start snackbar" closeable></md-snackbar>
<md-snackbar id="snack-b" ref="snackB" position="bottom" message="Bottom snackbar (default)" closeable></md-snackbar>
<md-snackbar id="snack-be" ref="snackBe" position="bottom-end" message="Bottom-end snackbar" closeable></md-snackbar>
</template><script>
import '@awc-ui/core/define';
let snackTs;
let snackT;
let snackTe;
let snackS;
let snackC;
let snackE;
let snackBs;
let snackB;
let snackBe;
</script>
<md-button variant="outlined" on:click={() => snackTs?.show()}>top-start</md-button>
<md-button variant="outlined" on:click={() => snackT?.show()}>top</md-button>
<md-button variant="outlined" on:click={() => snackTe?.show()}>top-end</md-button>
<md-button variant="outlined" on:click={() => snackS?.show()}>start</md-button>
<md-button variant="outlined" on:click={() => snackC?.show()}>center</md-button>
<md-button variant="outlined" on:click={() => snackE?.show()}>end</md-button>
<md-button variant="outlined" on:click={() => snackBs?.show()}>bottom-start</md-button>
<md-button variant="filled" on:click={() => snackB?.show()}>bottom</md-button>
<md-button variant="outlined" on:click={() => snackBe?.show()}>bottom-end</md-button>
<md-snackbar id="snack-ts" bind:this={snackTs} position="top-start" message="Top-start snackbar" closeable></md-snackbar>
<md-snackbar id="snack-t" bind:this={snackT} position="top" message="Top snackbar" closeable></md-snackbar>
<md-snackbar id="snack-te" bind:this={snackTe} position="top-end" message="Top-end snackbar" closeable></md-snackbar>
<md-snackbar id="snack-s" bind:this={snackS} position="start" message="Start snackbar" closeable></md-snackbar>
<md-snackbar id="snack-c" bind:this={snackC} position="center" message="Center snackbar" closeable></md-snackbar>
<md-snackbar id="snack-e" bind:this={snackE} position="end" message="End snackbar" closeable></md-snackbar>
<md-snackbar id="snack-bs" bind:this={snackBs} position="bottom-start" message="Bottom-start snackbar" closeable></md-snackbar>
<md-snackbar id="snack-b" bind:this={snackB} position="bottom" message="Bottom snackbar (default)" closeable></md-snackbar>
<md-snackbar id="snack-be" bind:this={snackBe} position="bottom-end" message="Bottom-end snackbar" closeable></md-snackbar>The default slot replaces message when you need light inline markup. Keep it
plain text-ish — M3 forbids icons and inline links in a snackbar.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-button variant="filled">Slotted message</md-button>
<md-snackbar id="snack-slotted" closeable>
File <strong style="color: var(--md-sys-color-inverse-primary, #D0BCFF)">report.pdf</strong> uploaded
</md-snackbar>
<script type="module">
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-slotted').show());
</script>import { useRef } from 'react';
import { MdButton, MdSnackbar } from '@awc-ui/react';
export function Demo() {
const snackSlottedRef = useRef(null);
return (
<>
<MdButton variant="filled" onClick={() => snackSlottedRef.current?.show()}>Slotted message</MdButton>
<MdSnackbar id="snack-slotted" ref={snackSlottedRef} closeable>
File <strong style={{ color: 'var(--md-sys-color-inverse-primary, #D0BCFF)' }}>report.pdf</strong> uploaded
</MdSnackbar>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-button variant="filled" (click)="snackSlotted.show()">Slotted message</md-button>
<md-snackbar id="snack-slotted" #snackSlotted closeable>
File <strong style="color: var(--md-sys-color-inverse-primary, #D0BCFF)">report.pdf</strong> uploaded
</md-snackbar><script setup>
import { ref } from 'vue';
import '@awc-ui/core/define';
const snackSlotted = ref(null);
</script>
<template>
<md-button variant="filled" @click="snackSlotted?.show()">Slotted message</md-button>
<md-snackbar id="snack-slotted" ref="snackSlotted" closeable>
File <strong style="color: var(--md-sys-color-inverse-primary, #D0BCFF)">report.pdf</strong> uploaded
</md-snackbar>
</template><script>
import '@awc-ui/core/define';
let snackSlotted;
</script>
<md-button variant="filled" on:click={() => snackSlotted?.show()}>Slotted message</md-button>
<md-snackbar id="snack-slotted" bind:this={snackSlotted} closeable>
File <strong style="color: var(--md-sys-color-inverse-primary, #D0BCFF)">report.pdf</strong> uploaded
</md-snackbar>The action and close slots let you supply your own controls when the
built-in text button and icon button aren’t enough — but keep the action a
text button.
One element shows one message. Showing two at once violates M3, so serialise
them yourself: reuse a single md-snackbar, set message before each
show(), and wait for mdClose before moving on.
<md-snackbar id="bar" closeable></md-snackbar>
<script type="module">
import '@awc-ui/core/define';
const bar = document.getElementById('bar');
const queue = [];
window.toast = async function toast(message) {
queue.push(message);
if (queue.length > 1) return;
while (queue.length) {
bar.message = queue[0];
bar.show();
await new Promise((r) => bar.addEventListener('mdClose', r, { once: true }));
queue.shift();
}
};
</script>import { MdSnackbar } from '@awc-ui/react';
import { useCallback, useRef, useState } from 'react';
export function Toaster() {
const bar = useRef<HTMLMdSnackbarElement>(null);
const queue = useRef<string[]>([]);
const [message, setMessage] = useState('');
// The element sets `open` back to false itself, so `open={queue.length > 0}`
// would go stale. Render the message, then call show() on the ref.
const next = useCallback(() => {
if (!queue.current.length) return;
setMessage(queue.current[0]);
requestAnimationFrame(() => bar.current?.show());
}, []);
const toast = useCallback(
(msg: string) => {
queue.current.push(msg);
if (queue.current.length === 1) next();
},
[next],
);
return (
<MdSnackbar
ref={bar}
message={message}
closeable
onMdClose={() => {
queue.current.shift();
// Let the exit transition finish before the next one slides in.
setTimeout(next, 250);
}}
/>
);
}import { Component, ElementRef, ViewChild } from '@angular/core';
@Component({
selector: 'app-toaster',
template: `
<md-snackbar #bar [message]="message" closeable
(mdClose)="onClose()"></md-snackbar>
`,
})
export class ToasterComponent {
@ViewChild('bar') bar!: ElementRef<HTMLMdSnackbarElement>;
message = '';
private queue: string[] = [];
// No [open] binding: the element writes open back to false, so Angular's
// Object.is check would skip the next write and nothing would reopen.
toast(message: string) {
this.queue.push(message);
if (this.queue.length === 1) this.next();
}
private next() {
if (!this.queue.length) return;
this.message = this.queue[0];
setTimeout(() => this.bar.nativeElement.show());
}
onClose() {
this.queue.shift();
setTimeout(() => this.next(), 250);
}
}<script setup lang="ts">
import { nextTick, ref } from 'vue';
import '@awc-ui/core/define';
const bar = ref<HTMLMdSnackbarElement | null>(null);
const message = ref('');
const queue: string[] = [];
// No :open binding: the element writes open back to false, so Vue's
// next !== prev patch check would skip the next write.
function toast(msg: string) {
queue.push(msg);
if (queue.length === 1) next();
}
async function next() {
if (!queue.length) return;
message.value = queue[0];
await nextTick();
bar.value?.show();
}
function onClose() {
queue.shift();
setTimeout(next, 250);
}
</script>
<template>
<md-snackbar ref="bar" :message="message" closeable @mdClose="onClose" />
</template><script lang="ts">
import { tick } from 'svelte';
import '@awc-ui/core/define';
let bar: HTMLMdSnackbarElement;
let message = '';
const queue: string[] = [];
// No open={...} binding: the element writes open back to false, so Svelte's
// x !== (x = ...) guard would skip the next write.
export function toast(msg: string) {
queue.push(msg);
if (queue.length === 1) next();
}
async function next() {
if (!queue.length) return;
message = queue[0];
await tick();
bar.show();
}
function onClose() {
queue.shift();
setTimeout(next, 250);
}
</script>
<md-snackbar bind:this={bar} {message} closeable on:mdClose={onClose} />| Event | Cancelable | Detail | Fires |
|---|---|---|---|
mdOpen | no | void | Shown |
mdAction | no | void | The action button was used |
mdClose | no | { reason: 'auto' | 'action' | 'close' } | Hidden — branch on the reason |
The action is not a dismiss button. Clicking it emits mdAction, leaves the
snackbar open, and restarts the auto-hide timer. That is deliberate — an
Undo that vanished the instant you pressed it would give you no confirmation
that anything happened — but it means mdClose.detail.reason === 'action' is
only reachable if you call hide('action') yourself.
So the two events are not interchangeable. mdAction says the button was
pressed; mdClose says the snackbar is gone, and here is which of the three
exits took it — 'action' from your own hide('action'), 'auto' from the
timer, 'close' from the X icon or Escape. Only mdClose can tell those
apart, which is why the commit belongs there.
| You want to… | Listen to |
|---|---|
| Reverse the operation (Undo was pressed) | mdClose, detail.reason === 'action' |
| Commit the operation (it timed out or was dismissed) | mdClose, reason is 'auto' or 'close' |
| React to the action button itself (analytics, a spinner) | mdAction |
| Actually dismiss on the action | call hide('action') from mdAction |
| Know it appeared | mdOpen |
All three events bubble and are composed, so one delegated listener on an ancestor catches every snackbar on the page.
<md-button variant="filled" id="show">Archive message</md-button>
<md-snackbar id="bar" message="Message archived" action="Undo" closeable></md-snackbar>
<script type="module">
import '@awc-ui/core/define';
const bar = document.getElementById('bar');
document.getElementById('show').addEventListener('click', () => bar.show());
bar.addEventListener('mdOpen', () => console.log('shown'));
// The action does not dismiss — close it with the matching reason.
bar.addEventListener('mdAction', () => bar.hide('action'));
bar.addEventListener('mdClose', (e) => {
if (e.detail.reason === 'action') undoArchive();
else commitArchive();
});
</script>import { MdButton, MdSnackbar } from '@awc-ui/react';
import { useRef } from 'react';
export function ArchiveToast() {
const bar = useRef<HTMLMdSnackbarElement>(null);
return (
<>
<MdButton variant="filled" onClick={() => bar.current?.show()}>
Archive message
</MdButton>
<MdSnackbar
ref={bar}
message="Message archived"
action="Undo"
closeable
onMdOpen={() => console.log('shown')}
onMdAction={() => bar.current?.hide('action')}
onMdClose={(e) => {
if (e.detail.reason === 'action') undoArchive();
else commitArchive();
}}
/>
</>
);
}import { Component, ElementRef, ViewChild } from '@angular/core';
@Component({
selector: 'app-archive-toast',
template: `
<md-button variant="filled" (click)="bar.show()">Archive message</md-button>
<md-snackbar
#bar
message="Message archived"
action="Undo"
closeable
(mdOpen)="onOpen()"
(mdAction)="bar.hide('action')"
(mdClose)="onClose($event)"
></md-snackbar>
`,
})
export class ArchiveToastComponent {
@ViewChild('bar') barRef!: ElementRef<HTMLMdSnackbarElement>;
onOpen() {
console.log('shown');
}
onClose(e: CustomEvent<{ reason: 'auto' | 'action' | 'close' }>) {
if (e.detail.reason === 'action') this.undoArchive();
else this.commitArchive();
}
}<script setup lang="ts">
import { ref } from 'vue';
import '@awc-ui/core/define';
const bar = ref<HTMLMdSnackbarElement | null>(null);
function onClose(e: CustomEvent<{ reason: 'auto' | 'action' | 'close' }>) {
if (e.detail.reason === 'action') undoArchive();
else commitArchive();
}
</script>
<template>
<md-button variant="filled" @click="bar?.show()">Archive message</md-button>
<md-snackbar
ref="bar"
message="Message archived"
action="Undo"
closeable
@mdOpen="console.log('shown')"
@mdAction="bar?.hide('action')"
@mdClose="onClose"
/>
</template><script lang="ts">
import '@awc-ui/core/define';
let bar: HTMLMdSnackbarElement;
function onClose(e: CustomEvent<{ reason: 'auto' | 'action' | 'close' }>) {
if (e.detail.reason === 'action') undoArchive();
else commitArchive();
}
</script>
<md-button variant="filled" on:click={() => bar.show()}>Archive message</md-button>
<md-snackbar
bind:this={bar}
message="Message archived"
action="Undo"
closeable
on:mdOpen={() => console.log('shown')}
on:mdAction={() => bar.hide('action')}
on:mdClose={onClose}
/>Wired against a real list — hold the pending row, and reverse it only when the
close reason is action:
<md-snackbar id="bar" action="Undo" position="bottom-start"></md-snackbar>
<script type="module">
const bar = document.getElementById('bar');
let pending;
function archive(item) {
hideRow(item);
pending = item;
bar.message = 'Message archived';
bar.show();
}
// The action never self-dismisses — close it with the matching reason.
bar.addEventListener('mdAction', () => bar.hide('action'));
bar.addEventListener('mdClose', (e) => {
if (e.detail.reason === 'action') restoreRow(pending); // Undo pressed
else commitArchive(pending); // auto / close
pending = null;
});
</script>import { MdSnackbar } from '@awc-ui/react';
import { useRef, useState } from 'react';
export function ArchiveToast() {
const bar = useRef<HTMLMdSnackbarElement>(null);
const [pending, setPending] = useState<Item | null>(null);
function archive(item: Item) {
hideRow(item);
setPending(item);
bar.current?.show();
}
return (
<MdSnackbar
ref={bar}
message="Message archived"
action="Undo"
position="bottom-start"
onMdAction={() => bar.current?.hide('action')}
onMdClose={(e) => {
if (e.detail.reason === 'action') restoreRow(pending!);
else commitArchive(pending!);
setPending(null);
}}
/>
);
}import { Component, ElementRef, ViewChild } from '@angular/core';
@Component({
selector: 'app-archive-toast',
template: `
<md-snackbar #bar message="Message archived" action="Undo"
position="bottom-start" (mdAction)="onAction()"
(mdClose)="onClose($event)"></md-snackbar>
`,
})
export class ArchiveToastComponent {
@ViewChild('bar') bar!: ElementRef<
HTMLElement & { show(): void; hide(reason?: string): void }
>;
private pending: Item | null = null;
archive(item: Item) {
this.hideRow(item);
this.pending = item;
this.bar.nativeElement.show();
}
onAction() {
this.bar.nativeElement.hide('action');
}
onClose(e: CustomEvent<{ reason: 'auto' | 'action' | 'close' }>) {
if (e.detail.reason === 'action') this.restoreRow(this.pending!);
else this.commitArchive(this.pending!);
this.pending = null;
}
}<script setup lang="ts">
import { ref } from 'vue';
const bar = ref<(HTMLElement & { show(): void; hide(r?: string): void }) | null>(null);
const pending = ref<Item | null>(null);
function archive(item: Item) {
hideRow(item);
pending.value = item;
bar.value?.show();
}
function onClose(e: CustomEvent<{ reason: 'auto' | 'action' | 'close' }>) {
if (e.detail.reason === 'action') restoreRow(pending.value!);
else commitArchive(pending.value!);
pending.value = null;
}
</script>
<template>
<md-snackbar
ref="bar"
message="Message archived"
action="Undo"
position="bottom-start"
@mdAction="bar?.hide('action')"
@mdClose="onClose"
/>
</template><script lang="ts">
let bar: HTMLElement & { show(): void; hide(reason?: string): void };
let pending: Item | null = null;
function archive(item: Item) {
hideRow(item);
pending = item;
bar.show();
}
function onClose(e: CustomEvent<{ reason: 'auto' | 'action' | 'close' }>) {
if (e.detail.reason === 'action') restoreRow(pending!);
else commitArchive(pending!);
pending = null;
}
</script>
<md-snackbar
bind:this={bar}
message="Message archived"
action="Undo"
position="bottom-start"
on:mdAction={() => bar.hide('action')}
on:mdClose={onClose}
/>| Property | Attribute | Type | Default | Reflects |
|---|---|---|---|---|
open | open | boolean | false | Yes |
message | message | string | '' | — |
action | action | string | '' | — |
closeable | closeable | boolean | false | Yes |
autoHide | auto-hide | boolean | true | — |
autoHideDuration | auto-hide-duration | number | 4000 | — |
position | position | | 'bottom' | 'bottom-start' | 'bottom-end' | 'top' | 'top-start' | 'top-end' | 'start' | 'end' | 'center' | 'bottom' | Yes |
stacked | stacked | boolean | false | Yes |
politeness | politeness | 'polite' | 'assertive' | 'polite' | Yes |
dismissLabel | dismiss-label | string | 'Dismiss' | — |
density | density | 0 | -1 | -2 | -3 | -4 | 0 | Yes |
| Method | Parameters |
|---|---|
show() | none |
hide() | reason: 'auto' | 'action' | 'close' = 'close' |
| Slot | Description |
|---|---|
(default) | — |
action | Custom action element |
close | Custom close element |
Override on the host element for per-instance theming:
| Property | Description |
|---|---|
--md-snackbar-container-color | Surface background |
--md-snackbar-message-color | Supporting text color |
--md-snackbar-action-color | Action button text color |
--md-snackbar-close-color | Close icon color |
--md-snackbar-container-shape | Border radius |
--md-snackbar-min-width | Minimum inline size |
--md-snackbar-max-width | Maximum inline size |
Style internal elements through shadow DOM with ::part():
| Part | Description |
|---|---|
surface | Outer container |
message | Supporting text wrapper |
actions | Action button wrapper |
close | Close button wrapper |
close-icon | — |
politeness controls interruption.
Reserve assertive for genuinely urgent messages — it interrupts screen-reader
users mid-sentence.auto-hide-duration, and make sure
the same action is reachable elsewhere in the UI.politeness="polite" (the default) renders role="status" /
aria-live="polite"; politeness="assertive" renders role="alert" /
aria-live="assertive". Either way aria-atomic="true" makes the whole
message re-announce rather than just the changed words.Escape closes it, but only when closeable is set — an X-less snackbar has
no dismiss affordance to trigger.dismiss-label (default "Dismiss").Show the polite one below, then hover its surface and watch the four seconds
stop counting. Tab into it — the timer stays paused, the close button announces
its dismiss-label, and Escape dismisses it. The assertive one has no
timer to pause (it sets auto-hide="false"), so use it to hear role="alert"
interrupt instead.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-button variant="filled">polite (role=status)</md-button>
<md-button variant="outlined">assertive (role=alert)</md-button>
<md-snackbar id="snack-a11y-polite" message="Draft saved" action="Undo" closeable dismiss-label="Dismiss"></md-snackbar>
<md-snackbar id="snack-a11y-assertive" message="No connection" action="Retry" closeable politeness="assertive" auto-hide="false" dismiss-label="Dismiss notification"></md-snackbar>
<script type="module">
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-a11y-polite').show());
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-a11y-assertive').show());
</script>import { useRef } from 'react';
import { MdButton, MdSnackbar } from '@awc-ui/react';
export function Demo() {
const snackA11yPoliteRef = useRef(null);
const snackA11yAssertiveRef = useRef(null);
return (
<>
<MdButton variant="filled" onClick={() => snackA11yPoliteRef.current?.show()}>polite (role=status)</MdButton>
<MdButton variant="outlined" onClick={() => snackA11yAssertiveRef.current?.show()}>assertive (role=alert)</MdButton>
<MdSnackbar id="snack-a11y-polite" ref={snackA11yPoliteRef} message="Draft saved" action="Undo" closeable dismissLabel="Dismiss"></MdSnackbar>
<MdSnackbar id="snack-a11y-assertive" ref={snackA11yAssertiveRef} message="No connection" action="Retry" closeable politeness="assertive" autoHide="false" dismissLabel="Dismiss notification"></MdSnackbar>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-button variant="filled" (click)="snackA11yPolite.show()">polite (role=status)</md-button>
<md-button variant="outlined" (click)="snackA11yAssertive.show()">assertive (role=alert)</md-button>
<md-snackbar id="snack-a11y-polite" #snackA11yPolite message="Draft saved" action="Undo" closeable dismiss-label="Dismiss"></md-snackbar>
<md-snackbar id="snack-a11y-assertive" #snackA11yAssertive message="No connection" action="Retry" closeable politeness="assertive" auto-hide="false" dismiss-label="Dismiss notification"></md-snackbar><script setup>
import { ref } from 'vue';
import '@awc-ui/core/define';
const snackA11yPolite = ref(null);
const snackA11yAssertive = ref(null);
</script>
<template>
<md-button variant="filled" @click="snackA11yPolite?.show()">polite (role=status)</md-button>
<md-button variant="outlined" @click="snackA11yAssertive?.show()">assertive (role=alert)</md-button>
<md-snackbar id="snack-a11y-polite" ref="snackA11yPolite" message="Draft saved" action="Undo" closeable dismiss-label="Dismiss"></md-snackbar>
<md-snackbar id="snack-a11y-assertive" ref="snackA11yAssertive" message="No connection" action="Retry" closeable politeness="assertive" auto-hide="false" dismiss-label="Dismiss notification"></md-snackbar>
</template><script>
import '@awc-ui/core/define';
let snackA11yPolite;
let snackA11yAssertive;
</script>
<md-button variant="filled" on:click={() => snackA11yPolite?.show()}>polite (role=status)</md-button>
<md-button variant="outlined" on:click={() => snackA11yAssertive?.show()}>assertive (role=alert)</md-button>
<md-snackbar id="snack-a11y-polite" bind:this={snackA11yPolite} message="Draft saved" action="Undo" closeable dismiss-label="Dismiss"></md-snackbar>
<md-snackbar id="snack-a11y-assertive" bind:this={snackA11yAssertive} message="No connection" action="Retry" closeable politeness="assertive" auto-hide="false" dismiss-label="Dismiss notification"></md-snackbar>The assertive one sets auto-hide="false" and pairs it with closeable, exactly
as the callout above insists — but “no auto-hide” only holds until its action is
used. Press Retry and the component starts a 4000ms timer anyway, closing
itself with reason === 'auto'. Handle mdAction yourself if a message must
survive its own action.
RTL — the host is a full-viewport flex overlay, so position resolves
against the writing direction the element inherits. Set dir on an ancestor and
nothing else in the markup changes:
<div dir="rtl"> <md-snackbar position="bottom-start" message="…" closeable></md-snackbar></div>Press both and watch the corners swap — bottom-start is bottom-left under
ltr and bottom-right under rtl. See the
RTL story and
RTL.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div style="display:grid;grid-template-columns:auto 1fr;gap:14px 16px;align-items:center">
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">ltr</span>
<div dir="ltr" style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="filled">bottom-start</md-button>
<md-snackbar id="snack-rtl-a" position="bottom-start" message="dir=ltr — this lands bottom-left" closeable></md-snackbar>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">rtl</span>
<div dir="rtl" style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="filled">bottom-start</md-button>
<md-snackbar id="snack-rtl-b" position="bottom-start" message="dir=rtl — this lands bottom-right" closeable></md-snackbar>
</div>
</div>
<script type="module">
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-rtl-a').show());
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-rtl-b').show());
</script>import { useRef } from 'react';
import { MdButton, MdSnackbar } from '@awc-ui/react';
export function Demo() {
const snackRtlARef = useRef(null);
const snackRtlBRef = useRef(null);
return (
<>
<div style={{ display: 'grid', gridTemplateColumns: 'auto 1fr', gap: '14px 16px', alignItems: 'center' }}>
<span style={{ inlineSize: '3.5rem', opacity: '.65', fontSize: '.75rem', fontFamily: 'ui-monospace,monospace' }}>ltr</span>
<div dir="ltr" style={{ display: 'flex', flexWrap: 'wrap', gap: '12px' }}>
<MdButton variant="filled" onClick={() => snackRtlARef.current?.show()}>bottom-start</MdButton>
<MdSnackbar id="snack-rtl-a" ref={snackRtlARef} position="bottom-start" message="dir=ltr — this lands bottom-left" closeable></MdSnackbar>
</div>
<span style={{ inlineSize: '3.5rem', opacity: '.65', fontSize: '.75rem', fontFamily: 'ui-monospace,monospace' }}>rtl</span>
<div dir="rtl" style={{ display: 'flex', flexWrap: 'wrap', gap: '12px' }}>
<MdButton variant="filled" onClick={() => snackRtlBRef.current?.show()}>bottom-start</MdButton>
<MdSnackbar id="snack-rtl-b" ref={snackRtlBRef} position="bottom-start" message="dir=rtl — this lands bottom-right" closeable></MdSnackbar>
</div>
</div>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<div style="display:grid;grid-template-columns:auto 1fr;gap:14px 16px;align-items:center">
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">ltr</span>
<div dir="ltr" style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="filled" (click)="snackRtlA.show()">bottom-start</md-button>
<md-snackbar id="snack-rtl-a" #snackRtlA position="bottom-start" message="dir=ltr — this lands bottom-left" closeable></md-snackbar>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">rtl</span>
<div dir="rtl" style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="filled" (click)="snackRtlB.show()">bottom-start</md-button>
<md-snackbar id="snack-rtl-b" #snackRtlB position="bottom-start" message="dir=rtl — this lands bottom-right" closeable></md-snackbar>
</div>
</div><script setup>
import { ref } from 'vue';
import '@awc-ui/core/define';
const snackRtlA = ref(null);
const snackRtlB = ref(null);
</script>
<template>
<div style="display:grid;grid-template-columns:auto 1fr;gap:14px 16px;align-items:center">
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">ltr</span>
<div dir="ltr" style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="filled" @click="snackRtlA?.show()">bottom-start</md-button>
<md-snackbar id="snack-rtl-a" ref="snackRtlA" position="bottom-start" message="dir=ltr — this lands bottom-left" closeable></md-snackbar>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">rtl</span>
<div dir="rtl" style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="filled" @click="snackRtlB?.show()">bottom-start</md-button>
<md-snackbar id="snack-rtl-b" ref="snackRtlB" position="bottom-start" message="dir=rtl — this lands bottom-right" closeable></md-snackbar>
</div>
</div>
</template><script>
import '@awc-ui/core/define';
let snackRtlA;
let snackRtlB;
</script>
<div style="display:grid;grid-template-columns:auto 1fr;gap:14px 16px;align-items:center">
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">ltr</span>
<div dir="ltr" style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="filled" on:click={() => snackRtlA?.show()}>bottom-start</md-button>
<md-snackbar id="snack-rtl-a" bind:this={snackRtlA} position="bottom-start" message="dir=ltr — this lands bottom-left" closeable></md-snackbar>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">rtl</span>
<div dir="rtl" style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="filled" on:click={() => snackRtlB?.show()}>bottom-start</md-button>
<md-snackbar id="snack-rtl-b" bind:this={snackRtlB} position="bottom-start" message="dir=rtl — this lands bottom-right" closeable></md-snackbar>
</div>
</div>start / end positions mirrorOf the nine values, six carry a direction — start, end, top-start,
top-end, bottom-start, bottom-end — and all six flip. The other three —
top, bottom (the default) and center — are centred on the inline axis and
look identical in both directions. Both rows below are inside dir="rtl".
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div dir="rtl" style="display:grid;grid-template-columns:auto 1fr;gap:14px 16px;align-items:center">
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">mirrors</span>
<div style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="filled">start</md-button>
<md-button variant="filled">end</md-button>
<md-snackbar id="snack-rtl-start" position="start" message="position=start — inline start, so the right edge here" closeable></md-snackbar>
<md-snackbar id="snack-rtl-end" position="end" message="position=end — inline end, so the left edge here" closeable></md-snackbar>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">neutral</span>
<div style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="outlined">bottom</md-button>
<md-button variant="outlined">center</md-button>
<md-snackbar id="snack-rtl-bottom" position="bottom" message="position=bottom — centred, identical in both directions" closeable></md-snackbar>
<md-snackbar id="snack-rtl-center" position="center" message="position=center — centred, identical in both directions" closeable></md-snackbar>
</div>
</div>
<script type="module">
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-rtl-start').show());
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-rtl-end').show());
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-rtl-bottom').show());
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-rtl-center').show());
</script>import { useRef } from 'react';
import { MdButton, MdSnackbar } from '@awc-ui/react';
export function Demo() {
const snackRtlStartRef = useRef(null);
const snackRtlEndRef = useRef(null);
const snackRtlBottomRef = useRef(null);
const snackRtlCenterRef = useRef(null);
return (
<>
<div dir="rtl" style={{ display: 'grid', gridTemplateColumns: 'auto 1fr', gap: '14px 16px', alignItems: 'center' }}>
<span style={{ inlineSize: '3.5rem', opacity: '.65', fontSize: '.75rem', fontFamily: 'ui-monospace,monospace' }}>mirrors</span>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '12px' }}>
<MdButton variant="filled" onClick={() => snackRtlStartRef.current?.show()}>start</MdButton>
<MdButton variant="filled" onClick={() => snackRtlEndRef.current?.show()}>end</MdButton>
<MdSnackbar id="snack-rtl-start" ref={snackRtlStartRef} position="start" message="position=start — inline start, so the right edge here" closeable></MdSnackbar>
<MdSnackbar id="snack-rtl-end" ref={snackRtlEndRef} position="end" message="position=end — inline end, so the left edge here" closeable></MdSnackbar>
</div>
<span style={{ inlineSize: '3.5rem', opacity: '.65', fontSize: '.75rem', fontFamily: 'ui-monospace,monospace' }}>neutral</span>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '12px' }}>
<MdButton variant="outlined" onClick={() => snackRtlBottomRef.current?.show()}>bottom</MdButton>
<MdButton variant="outlined" onClick={() => snackRtlCenterRef.current?.show()}>center</MdButton>
<MdSnackbar id="snack-rtl-bottom" ref={snackRtlBottomRef} position="bottom" message="position=bottom — centred, identical in both directions" closeable></MdSnackbar>
<MdSnackbar id="snack-rtl-center" ref={snackRtlCenterRef} position="center" message="position=center — centred, identical in both directions" closeable></MdSnackbar>
</div>
</div>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<div dir="rtl" style="display:grid;grid-template-columns:auto 1fr;gap:14px 16px;align-items:center">
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">mirrors</span>
<div style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="filled" (click)="snackRtlStart.show()">start</md-button>
<md-button variant="filled" (click)="snackRtlEnd.show()">end</md-button>
<md-snackbar id="snack-rtl-start" #snackRtlStart position="start" message="position=start — inline start, so the right edge here" closeable></md-snackbar>
<md-snackbar id="snack-rtl-end" #snackRtlEnd position="end" message="position=end — inline end, so the left edge here" closeable></md-snackbar>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">neutral</span>
<div style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="outlined" (click)="snackRtlBottom.show()">bottom</md-button>
<md-button variant="outlined" (click)="snackRtlCenter.show()">center</md-button>
<md-snackbar id="snack-rtl-bottom" #snackRtlBottom position="bottom" message="position=bottom — centred, identical in both directions" closeable></md-snackbar>
<md-snackbar id="snack-rtl-center" #snackRtlCenter position="center" message="position=center — centred, identical in both directions" closeable></md-snackbar>
</div>
</div><script setup>
import { ref } from 'vue';
import '@awc-ui/core/define';
const snackRtlStart = ref(null);
const snackRtlEnd = ref(null);
const snackRtlBottom = ref(null);
const snackRtlCenter = ref(null);
</script>
<template>
<div dir="rtl" style="display:grid;grid-template-columns:auto 1fr;gap:14px 16px;align-items:center">
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">mirrors</span>
<div style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="filled" @click="snackRtlStart?.show()">start</md-button>
<md-button variant="filled" @click="snackRtlEnd?.show()">end</md-button>
<md-snackbar id="snack-rtl-start" ref="snackRtlStart" position="start" message="position=start — inline start, so the right edge here" closeable></md-snackbar>
<md-snackbar id="snack-rtl-end" ref="snackRtlEnd" position="end" message="position=end — inline end, so the left edge here" closeable></md-snackbar>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">neutral</span>
<div style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="outlined" @click="snackRtlBottom?.show()">bottom</md-button>
<md-button variant="outlined" @click="snackRtlCenter?.show()">center</md-button>
<md-snackbar id="snack-rtl-bottom" ref="snackRtlBottom" position="bottom" message="position=bottom — centred, identical in both directions" closeable></md-snackbar>
<md-snackbar id="snack-rtl-center" ref="snackRtlCenter" position="center" message="position=center — centred, identical in both directions" closeable></md-snackbar>
</div>
</div>
</template><script>
import '@awc-ui/core/define';
let snackRtlStart;
let snackRtlEnd;
let snackRtlBottom;
let snackRtlCenter;
</script>
<div dir="rtl" style="display:grid;grid-template-columns:auto 1fr;gap:14px 16px;align-items:center">
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">mirrors</span>
<div style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="filled" on:click={() => snackRtlStart?.show()}>start</md-button>
<md-button variant="filled" on:click={() => snackRtlEnd?.show()}>end</md-button>
<md-snackbar id="snack-rtl-start" bind:this={snackRtlStart} position="start" message="position=start — inline start, so the right edge here" closeable></md-snackbar>
<md-snackbar id="snack-rtl-end" bind:this={snackRtlEnd} position="end" message="position=end — inline end, so the left edge here" closeable></md-snackbar>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">neutral</span>
<div style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="outlined" on:click={() => snackRtlBottom?.show()}>bottom</md-button>
<md-button variant="outlined" on:click={() => snackRtlCenter?.show()}>center</md-button>
<md-snackbar id="snack-rtl-bottom" bind:this={snackRtlBottom} position="bottom" message="position=bottom — centred, identical in both directions" closeable></md-snackbar>
<md-snackbar id="snack-rtl-center" bind:this={snackRtlCenter} position="center" message="position=center — centred, identical in both directions" closeable></md-snackbar>
</div>
</div>Each rung compacts padding and text, taking the surface
48 → 45 → 42 → 39 → 36px tall. Width follows only until it meets
--md-snackbar-min-width: in the demo below it goes 322 → 309 → 288px and then
stops, because 288px is the floor.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div style="display:grid;grid-template-columns:auto 1fr;gap:14px 16px;align-items:center">
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">0</span>
<div style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="filled">show</md-button>
<md-snackbar id="snack-d0" density="0" message="Message archived at density 0" action="Undo" closeable></md-snackbar>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-1</span>
<div style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="outlined">show</md-button>
<md-snackbar id="snack-d1" density="-1" message="Message archived at density -1" action="Undo" closeable></md-snackbar>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-2</span>
<div style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="outlined">show</md-button>
<md-snackbar id="snack-d2" density="-2" message="Message archived at density -2" action="Undo" closeable></md-snackbar>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-3</span>
<div style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="outlined">show</md-button>
<md-snackbar id="snack-d3" density="-3" message="Message archived at density -3" action="Undo" closeable></md-snackbar>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-4</span>
<div style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="outlined">show</md-button>
<md-snackbar id="snack-d4" density="-4" message="Message archived at density -4" action="Undo" closeable></md-snackbar>
</div>
</div>
<script type="module">
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-d0').show());
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-d1').show());
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-d2').show());
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-d3').show());
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-d4').show());
</script>import { useRef } from 'react';
import { MdButton, MdSnackbar } from '@awc-ui/react';
export function Demo() {
const snackD0Ref = useRef(null);
const snackD1Ref = useRef(null);
const snackD2Ref = useRef(null);
const snackD3Ref = useRef(null);
const snackD4Ref = useRef(null);
return (
<>
<div style={{ display: 'grid', gridTemplateColumns: 'auto 1fr', gap: '14px 16px', alignItems: 'center' }}>
<span style={{ inlineSize: '3.5rem', opacity: '.65', fontSize: '.75rem', fontFamily: 'ui-monospace,monospace' }}>0</span>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '12px' }}>
<MdButton variant="filled" onClick={() => snackD0Ref.current?.show()}>show</MdButton>
<MdSnackbar id="snack-d0" ref={snackD0Ref} density="0" message="Message archived at density 0" action="Undo" closeable></MdSnackbar>
</div>
<span style={{ inlineSize: '3.5rem', opacity: '.65', fontSize: '.75rem', fontFamily: 'ui-monospace,monospace' }}>-1</span>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '12px' }}>
<MdButton variant="outlined" onClick={() => snackD1Ref.current?.show()}>show</MdButton>
<MdSnackbar id="snack-d1" ref={snackD1Ref} density="-1" message="Message archived at density -1" action="Undo" closeable></MdSnackbar>
</div>
<span style={{ inlineSize: '3.5rem', opacity: '.65', fontSize: '.75rem', fontFamily: 'ui-monospace,monospace' }}>-2</span>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '12px' }}>
<MdButton variant="outlined" onClick={() => snackD2Ref.current?.show()}>show</MdButton>
<MdSnackbar id="snack-d2" ref={snackD2Ref} density="-2" message="Message archived at density -2" action="Undo" closeable></MdSnackbar>
</div>
<span style={{ inlineSize: '3.5rem', opacity: '.65', fontSize: '.75rem', fontFamily: 'ui-monospace,monospace' }}>-3</span>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '12px' }}>
<MdButton variant="outlined" onClick={() => snackD3Ref.current?.show()}>show</MdButton>
<MdSnackbar id="snack-d3" ref={snackD3Ref} density="-3" message="Message archived at density -3" action="Undo" closeable></MdSnackbar>
</div>
<span style={{ inlineSize: '3.5rem', opacity: '.65', fontSize: '.75rem', fontFamily: 'ui-monospace,monospace' }}>-4</span>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '12px' }}>
<MdButton variant="outlined" onClick={() => snackD4Ref.current?.show()}>show</MdButton>
<MdSnackbar id="snack-d4" ref={snackD4Ref} density="-4" message="Message archived at density -4" action="Undo" closeable></MdSnackbar>
</div>
</div>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<div style="display:grid;grid-template-columns:auto 1fr;gap:14px 16px;align-items:center">
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">0</span>
<div style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="filled" (click)="snackD0.show()">show</md-button>
<md-snackbar id="snack-d0" #snackD0 density="0" message="Message archived at density 0" action="Undo" closeable></md-snackbar>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-1</span>
<div style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="outlined" (click)="snackD1.show()">show</md-button>
<md-snackbar id="snack-d1" #snackD1 density="-1" message="Message archived at density -1" action="Undo" closeable></md-snackbar>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-2</span>
<div style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="outlined" (click)="snackD2.show()">show</md-button>
<md-snackbar id="snack-d2" #snackD2 density="-2" message="Message archived at density -2" action="Undo" closeable></md-snackbar>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-3</span>
<div style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="outlined" (click)="snackD3.show()">show</md-button>
<md-snackbar id="snack-d3" #snackD3 density="-3" message="Message archived at density -3" action="Undo" closeable></md-snackbar>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-4</span>
<div style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="outlined" (click)="snackD4.show()">show</md-button>
<md-snackbar id="snack-d4" #snackD4 density="-4" message="Message archived at density -4" action="Undo" closeable></md-snackbar>
</div>
</div><script setup>
import { ref } from 'vue';
import '@awc-ui/core/define';
const snackD0 = ref(null);
const snackD1 = ref(null);
const snackD2 = ref(null);
const snackD3 = ref(null);
const snackD4 = ref(null);
</script>
<template>
<div style="display:grid;grid-template-columns:auto 1fr;gap:14px 16px;align-items:center">
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">0</span>
<div style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="filled" @click="snackD0?.show()">show</md-button>
<md-snackbar id="snack-d0" ref="snackD0" density="0" message="Message archived at density 0" action="Undo" closeable></md-snackbar>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-1</span>
<div style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="outlined" @click="snackD1?.show()">show</md-button>
<md-snackbar id="snack-d1" ref="snackD1" density="-1" message="Message archived at density -1" action="Undo" closeable></md-snackbar>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-2</span>
<div style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="outlined" @click="snackD2?.show()">show</md-button>
<md-snackbar id="snack-d2" ref="snackD2" density="-2" message="Message archived at density -2" action="Undo" closeable></md-snackbar>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-3</span>
<div style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="outlined" @click="snackD3?.show()">show</md-button>
<md-snackbar id="snack-d3" ref="snackD3" density="-3" message="Message archived at density -3" action="Undo" closeable></md-snackbar>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-4</span>
<div style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="outlined" @click="snackD4?.show()">show</md-button>
<md-snackbar id="snack-d4" ref="snackD4" density="-4" message="Message archived at density -4" action="Undo" closeable></md-snackbar>
</div>
</div>
</template><script>
import '@awc-ui/core/define';
let snackD0;
let snackD1;
let snackD2;
let snackD3;
let snackD4;
</script>
<div style="display:grid;grid-template-columns:auto 1fr;gap:14px 16px;align-items:center">
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">0</span>
<div style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="filled" on:click={() => snackD0?.show()}>show</md-button>
<md-snackbar id="snack-d0" bind:this={snackD0} density="0" message="Message archived at density 0" action="Undo" closeable></md-snackbar>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-1</span>
<div style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="outlined" on:click={() => snackD1?.show()}>show</md-button>
<md-snackbar id="snack-d1" bind:this={snackD1} density="-1" message="Message archived at density -1" action="Undo" closeable></md-snackbar>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-2</span>
<div style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="outlined" on:click={() => snackD2?.show()}>show</md-button>
<md-snackbar id="snack-d2" bind:this={snackD2} density="-2" message="Message archived at density -2" action="Undo" closeable></md-snackbar>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-3</span>
<div style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="outlined" on:click={() => snackD3?.show()}>show</md-button>
<md-snackbar id="snack-d3" bind:this={snackD3} density="-3" message="Message archived at density -3" action="Undo" closeable></md-snackbar>
</div>
<span style="inline-size:3.5rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-4</span>
<div style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="outlined" on:click={() => snackD4?.show()}>show</md-button>
<md-snackbar id="snack-d4" bind:this={snackD4} density="-4" message="Message archived at density -4" action="Undo" closeable></md-snackbar>
</div>
</div>A global data-density ancestor and dir are independent signals, and a local
density prop on one snackbar overrides the inherited rung without touching the
direction. Below, dir="rtl" data-density="-1" wraps both; the second snackbar
names -4 for itself and shrinks past its ancestor while still mirroring.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div dir="rtl" data-density="-1" style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="filled">inherits -1</md-button>
<md-button variant="outlined">local -4</md-button>
<md-snackbar id="snack-dd-a" position="bottom-start" message="dir=rtl, inherited density -1" action="Undo" closeable></md-snackbar>
<md-snackbar id="snack-dd-b" position="top-start" density="-4" message="dir=rtl, local density -4" action="Undo" closeable></md-snackbar>
</div>
<script type="module">
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-dd-a').show());
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-dd-b').show());
</script>import { useRef } from 'react';
import { MdButton, MdSnackbar } from '@awc-ui/react';
export function Demo() {
const snackDdARef = useRef(null);
const snackDdBRef = useRef(null);
return (
<>
<div dir="rtl" data-density="-1" style={{ display: 'flex', flexWrap: 'wrap', gap: '12px' }}>
<MdButton variant="filled" onClick={() => snackDdARef.current?.show()}>inherits -1</MdButton>
<MdButton variant="outlined" onClick={() => snackDdBRef.current?.show()}>local -4</MdButton>
<MdSnackbar id="snack-dd-a" ref={snackDdARef} position="bottom-start" message="dir=rtl, inherited density -1" action="Undo" closeable></MdSnackbar>
<MdSnackbar id="snack-dd-b" ref={snackDdBRef} position="top-start" density="-4" message="dir=rtl, local density -4" action="Undo" closeable></MdSnackbar>
</div>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<div dir="rtl" data-density="-1" style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="filled" (click)="snackDdA.show()">inherits -1</md-button>
<md-button variant="outlined" (click)="snackDdB.show()">local -4</md-button>
<md-snackbar id="snack-dd-a" #snackDdA position="bottom-start" message="dir=rtl, inherited density -1" action="Undo" closeable></md-snackbar>
<md-snackbar id="snack-dd-b" #snackDdB position="top-start" density="-4" message="dir=rtl, local density -4" action="Undo" closeable></md-snackbar>
</div><script setup>
import { ref } from 'vue';
import '@awc-ui/core/define';
const snackDdA = ref(null);
const snackDdB = ref(null);
</script>
<template>
<div dir="rtl" data-density="-1" style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="filled" @click="snackDdA?.show()">inherits -1</md-button>
<md-button variant="outlined" @click="snackDdB?.show()">local -4</md-button>
<md-snackbar id="snack-dd-a" ref="snackDdA" position="bottom-start" message="dir=rtl, inherited density -1" action="Undo" closeable></md-snackbar>
<md-snackbar id="snack-dd-b" ref="snackDdB" position="top-start" density="-4" message="dir=rtl, local density -4" action="Undo" closeable></md-snackbar>
</div>
</template><script>
import '@awc-ui/core/define';
let snackDdA;
let snackDdB;
</script>
<div dir="rtl" data-density="-1" style="display:flex;flex-wrap:wrap;gap:12px;">
<md-button variant="filled" on:click={() => snackDdA?.show()}>inherits -1</md-button>
<md-button variant="outlined" on:click={() => snackDdB?.show()}>local -4</md-button>
<md-snackbar id="snack-dd-a" bind:this={snackDdA} position="bottom-start" message="dir=rtl, inherited density -1" action="Undo" closeable></md-snackbar>
<md-snackbar id="snack-dd-b" bind:this={snackDdB} position="top-start" density="-4" message="dir=rtl, local density -4" action="Undo" closeable></md-snackbar>
</div>Density — density="-1…-4" locally overrides the inherited data-density
rung, taking the surface 48 → 36px tall. See Density.
i18n — translate message, action and dismiss-label. Translations run
longer, so re-check M3’s one-line rule and consider stacked. The
localization story
switches all three at once.
| Custom property | Purpose | Default |
|---|---|---|
--md-snackbar-container-color | Surface | inverse-surface |
--md-snackbar-container-shape | Corner radius | corner-extra-small (4px) |
--md-snackbar-message-color | Label | inverse-on-surface |
--md-snackbar-action-color | Action button label | inverse-primary |
--md-snackbar-close-color | Close affordance | inverse-on-surface |
--md-snackbar-min-width / --md-snackbar-max-width | Width bounds | 288px / 568px |
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-button variant="filled">Themed snackbar</md-button>
<md-snackbar id="snack-themed" message="Wider, squared, error-toned" action="Undo" closeable style="--md-snackbar-container-color: var(--md-sys-color-error); --md-snackbar-message-color: var(--md-sys-color-on-error); --md-snackbar-action-color: var(--md-sys-color-on-error); --md-snackbar-close-color: var(--md-sys-color-on-error); --md-snackbar-container-shape: 2px; --md-snackbar-max-width: 560px;"></md-snackbar>
<script type="module">
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-themed').show());
</script>import { useRef } from 'react';
import { MdButton, MdSnackbar } from '@awc-ui/react';
export function Demo() {
const snackThemedRef = useRef(null);
return (
<>
<MdButton variant="filled" onClick={() => snackThemedRef.current?.show()}>Themed snackbar</MdButton>
<MdSnackbar id="snack-themed" ref={snackThemedRef} message="Wider, squared, error-toned" action="Undo" closeable style={{ '--md-snackbar-container-color': 'var(--md-sys-color-error)', '--md-snackbar-message-color': 'var(--md-sys-color-on-error)', '--md-snackbar-action-color': 'var(--md-sys-color-on-error)', '--md-snackbar-close-color': 'var(--md-sys-color-on-error)', '--md-snackbar-container-shape': '2px', '--md-snackbar-max-width': '560px' }}></MdSnackbar>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-button variant="filled" (click)="snackThemed.show()">Themed snackbar</md-button>
<md-snackbar id="snack-themed" #snackThemed message="Wider, squared, error-toned" action="Undo" closeable style="--md-snackbar-container-color: var(--md-sys-color-error); --md-snackbar-message-color: var(--md-sys-color-on-error); --md-snackbar-action-color: var(--md-sys-color-on-error); --md-snackbar-close-color: var(--md-sys-color-on-error); --md-snackbar-container-shape: 2px; --md-snackbar-max-width: 560px;"></md-snackbar><script setup>
import { ref } from 'vue';
import '@awc-ui/core/define';
const snackThemed = ref(null);
</script>
<template>
<md-button variant="filled" @click="snackThemed?.show()">Themed snackbar</md-button>
<md-snackbar id="snack-themed" ref="snackThemed" message="Wider, squared, error-toned" action="Undo" closeable style="--md-snackbar-container-color: var(--md-sys-color-error); --md-snackbar-message-color: var(--md-sys-color-on-error); --md-snackbar-action-color: var(--md-sys-color-on-error); --md-snackbar-close-color: var(--md-sys-color-on-error); --md-snackbar-container-shape: 2px; --md-snackbar-max-width: 560px;"></md-snackbar>
</template><script>
import '@awc-ui/core/define';
let snackThemed;
</script>
<md-button variant="filled" on:click={() => snackThemed?.show()}>Themed snackbar</md-button>
<md-snackbar id="snack-themed" bind:this={snackThemed} message="Wider, squared, error-toned" action="Undo" closeable style="--md-snackbar-container-color: var(--md-sys-color-error); --md-snackbar-message-color: var(--md-sys-color-on-error); --md-snackbar-action-color: var(--md-sys-color-on-error); --md-snackbar-close-color: var(--md-sys-color-on-error); --md-snackbar-container-shape: 2px; --md-snackbar-max-width: 560px;"></md-snackbar>Check the result against the page behind it: the container is inverse-surface
precisely so it reads as not part of the page, and a themed one has to keep
that separation — the dark-theme
story is the other
half of that check.
CSS parts — surface, message, actions, close, plus close-icon,
which the built-in close button re-exports (exportparts="icon:close-icon") so
you can reach the glyph itself.
Use a part for what the custom properties cannot reach — border and radius on the surface, type style on the message, spacing between the controls:
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<style>
#snack-parts::part(surface) {
border-radius: 16px;
border: 2px solid var(--md-sys-color-outline-variant);
}
#snack-parts::part(message) { font-style: italic; }
#snack-parts::part(actions) { gap: 16px; }
</style>
<md-button variant="filled">Styled parts</md-button>
<md-snackbar id="snack-parts" message="Styled with ::part()" action="Nice" closeable></md-snackbar>
<script type="module">
// Give the trigger an id, then:
// trigger.addEventListener('click', () => document.getElementById('snack-parts').show());
</script>import { useRef } from 'react';
import { MdButton, MdSnackbar } from '@awc-ui/react';
export function Demo() {
const snackPartsRef = useRef(null);
return (
<>
<style>
#snack-parts::part(surface) {
border-radius: 16px;
border: 2px solid var(--md-sys-color-outline-variant);
}
#snack-parts::part(message) { font-style: italic; }
#snack-parts::part(actions) { gap: 16px; }
</style>
<MdButton variant="filled" onClick={() => snackPartsRef.current?.show()}>Styled parts</MdButton>
<MdSnackbar id="snack-parts" ref={snackPartsRef} message="Styled with ::part()" action="Nice" closeable></MdSnackbar>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<style>
#snack-parts::part(surface) {
border-radius: 16px;
border: 2px solid var(--md-sys-color-outline-variant);
}
#snack-parts::part(message) { font-style: italic; }
#snack-parts::part(actions) { gap: 16px; }
</style>
<md-button variant="filled" (click)="snackParts.show()">Styled parts</md-button>
<md-snackbar id="snack-parts" #snackParts message="Styled with ::part()" action="Nice" closeable></md-snackbar><script setup>
import { ref } from 'vue';
import '@awc-ui/core/define';
const snackParts = ref(null);
</script>
<template>
<style>
#snack-parts::part(surface) {
border-radius: 16px;
border: 2px solid var(--md-sys-color-outline-variant);
}
#snack-parts::part(message) { font-style: italic; }
#snack-parts::part(actions) { gap: 16px; }
</style>
<md-button variant="filled" @click="snackParts?.show()">Styled parts</md-button>
<md-snackbar id="snack-parts" ref="snackParts" message="Styled with ::part()" action="Nice" closeable></md-snackbar>
</template><script>
import '@awc-ui/core/define';
let snackParts;
</script>
<style>
#snack-parts::part(surface) {
border-radius: 16px;
border: 2px solid var(--md-sys-color-outline-variant);
}
#snack-parts::part(message) { font-style: italic; }
#snack-parts::part(actions) { gap: 16px; }
</style>
<md-button variant="filled" on:click={() => snackParts?.show()}>Styled parts</md-button>
<md-snackbar id="snack-parts" bind:this={snackParts} message="Styled with ::part()" action="Nice" closeable></md-snackbar>md-snackbar::part(message) { letter-spacing: 0.25px;}Keep the stock shape. Slight container transparency is fine if the text stays legible, but M3 warns against significantly altering the container shape.
md-dialog ·
md-tooltip ·
md-button ·
md-icon-button ·
md-bottom-sheet ·
md-progress-indicator
md-snackbarTwo artefacts to give your AI agent so it generates correct UI with this component. The per-component spec answers "how do I use this exact tag?". The main-llm spec answers "which tag should I pick in the first place?".
md-snackbar spec card
Identity · when to use / when NOT · decision cues · behavioural contract · do/don't · anti-patterns · full API. Paste into your agent when you're implementing with this component.
System-prompt preamble · decision matrix · token reference · page recipes · anti-patterns. Paste into the system prompt at the start of a piece of work.