Presentation
These widgets show state and identity rather than collect input: an avatar for a person or account, a banner for an in-context message, a shortcut label to draw a keyboard shortcut as keycaps, a spinner while work runs, a status page for an empty or error state, and a window title in a header bar.
What the preview does not show
The previews run the browser port, so behaviour Libadwaita drives natively (a spinner’s animation timing, a banner’s reveal transition) can be simplified here. The NativeScript port diverges in two places worth knowing: its avatar has no icon fallback, and its banner takes plain text rather than markup.
Avatar
Section titled “Avatar”Stand in for a person, account or entity. Ask for initials and give it a name, and it derives the initials and a stable background colour from that name; with either missing it falls back to a symbolic icon. Size scales it from an inline list glyph up to a full profile header.
using Gtk 4.0;using Adw 1;
Adw.Avatar avatar { text: "Ada Lovelace"; size: 96; show-initials: true; icon-name: "avatar-default-symbolic";}import { build } from './avatar.blp';
const { avatar } = build();avatar.text = 'Grace Hopper';<adw-avatar id="avatar" text="Ada Lovelace" size="96" show-initials icon-name="avatar-default-symbolic"></adw-avatar><script type="module"> import '@gjsify/adwaita-web';
const avatar = document.querySelector('#avatar'); avatar.setAttribute('text', 'Grace Hopper');</script>import { mountSharedTree } from '@gjsify/adwaita-web';import tree from './avatar.blp?shared-tree';
mountSharedTree(tree);
const avatar = document.querySelector('#avatar');avatar.setAttribute('text', 'Grace Hopper');<adw:Avatar xmlns="http://schemas.nativescript.org/tns.xsd" xmlns:adw="~/adw" id="avatar" text="Ada Lovelace" size="96" showInitials="true" iconName="avatar-default-symbolic"/>import { type Adw } from '@gjsify/adwaita-nativescript';import { Builder } from '@nativescript/core';
const avatar = Builder.load({ path: '~/views', name: 'avatar' }) as Adw.Avatar;avatar.text = 'Grace Hopper';import { type Adw } from '@gjsify/adwaita-nativescript';import { build } from '@gjsify/adwaita-nativescript/builder';import tree from './avatar.blp?shared-tree';
const avatar = build(tree) as Adw.Avatar;avatar.text = 'Grace Hopper';// mount(() => <AdwAvatar />, container) — from '@gjsify/gtk-host/solid'const AdwAvatar = () => ( <adw-avatar text="Ada Lovelace" size={96} showInitials iconName="avatar-default-symbolic" />);<!-- AdwAvatar.vue — mount(AdwAvatar, container) from '@gjsify/gtk-host/vue' --><template> <adw-avatar text="Ada Lovelace" :size="96" :show-initials="true" icon-name="avatar-default-symbolic" /></template>// createRoot(container).render(<AdwAvatar />) — from '@gjsify/gtk-host/react'const AdwAvatar = () => ( <adw-avatar text="Ada Lovelace" size={96} showInitials iconName="avatar-default-symbolic" />);Banner
Section titled “Banner”Surface one in-context status message across the top of a view: a metered connection, an offline state, a pending action. A banner takes a title and an optional action button, and it reveals and hides itself, so it only occupies space while the message applies.
using Gtk 4.0;using Adw 1;
Adw.Banner banner { title: _("Metered connection: updates paused"); button-label: _("Resume"); revealed: true;}import { build } from './banner.blp';
const { banner } = build();banner.title = 'Metered connection: 2 updates paused';<adw-banner id="banner" title="Metered connection: updates paused" button-label="Resume" revealed></adw-banner><script type="module"> import '@gjsify/adwaita-web';
const banner = document.querySelector('#banner'); banner.setAttribute('title', 'Metered connection: 2 updates paused');</script>import { mountSharedTree } from '@gjsify/adwaita-web';import tree from './banner.blp?shared-tree';
mountSharedTree(tree);
const banner = document.querySelector('#banner');banner.setAttribute('title', 'Metered connection: 2 updates paused');<adw:Banner xmlns="http://schemas.nativescript.org/tns.xsd" xmlns:adw="~/adw" id="banner" title="Metered connection: updates paused" buttonLabel="Resume" revealed="true"/>import { type Adw } from '@gjsify/adwaita-nativescript';import { Builder } from '@nativescript/core';
const banner = Builder.load({ path: '~/views', name: 'banner' }) as Adw.Banner;banner.title = 'Metered connection: 2 updates paused';import { type Adw } from '@gjsify/adwaita-nativescript';import { build } from '@gjsify/adwaita-nativescript/builder';import tree from './banner.blp?shared-tree';
const banner = build(tree) as Adw.Banner;banner.title = 'Metered connection: 2 updates paused';// mount(() => <AdwBanner />, container) — from '@gjsify/gtk-host/solid'const AdwBanner = () => ( <adw-banner title="Metered connection: updates paused" buttonLabel="Resume" revealed />);<!-- AdwBanner.vue — mount(AdwBanner, container) from '@gjsify/gtk-host/vue' --><template> <adw-banner title="Metered connection: updates paused" button-label="Resume" :revealed="true" /></template>// createRoot(container).render(<AdwBanner />) — from '@gjsify/gtk-host/react'const AdwBanner = () => ( <adw-banner title="Metered connection: updates paused" buttonLabel="Resume" revealed />);Shortcut Label
Section titled “Shortcut Label”Draw a keyboard shortcut as keycaps instead of writing it out in prose. You give it one accelerator string and it renders the keys in GTK’s fixed order, whatever order you wrote the modifiers in.
The four levels of an accelerator
A space lists alternatives, ... is a range, + means the shortcuts are pressed
in sequence, and & means they are held together. An empty accelerator shows the
disabled-text placeholder instead.
using Gtk 4.0;using Adw 1;
Gtk.Box shortcuts { orientation: horizontal; spacing: 24;
Adw.ShortcutLabel copy_shortcut { accelerator: "<Control>C"; }
Adw.ShortcutLabel { accelerator: "<Shift>A Home"; }
Adw.ShortcutLabel { accelerator: "<Alt>1...9"; }
Adw.ShortcutLabel { accelerator: "Control_L&Control_R"; }
Adw.ShortcutLabel { accelerator: ""; disabled-text: _("Disabled"); }}import { build } from './shortcut-label.blp';
const { copy_shortcut } = build();copy_shortcut.accelerator = '<Control><Shift>C';<gtk-box id="shortcuts" orientation="horizontal" spacing="24"> <adw-shortcut-label id="copy_shortcut" accelerator="<Control>C" ></adw-shortcut-label> <adw-shortcut-label accelerator="<Shift>A Home"></adw-shortcut-label> <adw-shortcut-label accelerator="<Alt>1...9"></adw-shortcut-label> <adw-shortcut-label accelerator="Control_L&Control_R" ></adw-shortcut-label> <adw-shortcut-label accelerator="" disabled-text="Disabled" ></adw-shortcut-label></gtk-box><script type="module"> import '@gjsify/adwaita-web';
const copy = document.querySelector('#copy_shortcut'); copy.setAttribute('accelerator', '<Control><Shift>C');</script>import { mountSharedTree } from '@gjsify/adwaita-web';import tree from './shortcut-label.blp?shared-tree';
mountSharedTree(tree);
const copy = document.querySelector('#copy_shortcut');copy.setAttribute('accelerator', '<Control><Shift>C');<gtk:Box xmlns="http://schemas.nativescript.org/tns.xsd" xmlns:adw="~/adw" xmlns:gtk="~/gtk" id="shortcuts" orientation="horizontal" spacing="24"> <adw:ShortcutLabel id="copy_shortcut" accelerator="<Control>C" /> <adw:ShortcutLabel accelerator="<Shift>A Home" /> <adw:ShortcutLabel accelerator="<Alt>1...9" /> <adw:ShortcutLabel accelerator="Control_L&Control_R" /> <adw:ShortcutLabel accelerator="" disabledText="Disabled" /></gtk:Box>import { type Adw, type Gtk } from '@gjsify/adwaita-nativescript';import { Builder } from '@nativescript/core';
const box = Builder.load({ path: '~/views', name: 'shortcut-label' }) as Gtk.Box;const copy = box.getViewById<Adw.ShortcutLabel>('copy_shortcut')!;copy.accelerator = '<Control><Shift>C';import { type Adw, type Gtk } from '@gjsify/adwaita-nativescript';import { build } from '@gjsify/adwaita-nativescript/builder';import tree from './shortcut-label.blp?shared-tree';
const box = build(tree) as Gtk.Box;const copy = box.getViewById<Adw.ShortcutLabel>('copy_shortcut')!;copy.accelerator = '<Control><Shift>C';// mount(() => <AdwShortcutLabel />, container) — from '@gjsify/gtk-host/solid'const AdwShortcutLabel = () => ( <adw-shortcut-label accelerator="<Control>C" />);<!-- AdwShortcutLabel.vue — mount(AdwShortcutLabel, container) from '@gjsify/gtk-host/vue' --><template> <adw-shortcut-label accelerator="<Control>C" /></template>// createRoot(container).render(<AdwShortcutLabel />) — from '@gjsify/gtk-host/react'const AdwShortcutLabel = () => ( <adw-shortcut-label accelerator="<Control>C" />);Spinner
Section titled “Spinner”Show that work is running when you cannot say how much is left. The spinner turns continuously while visible and scales to any size, from an inline glyph to a page-filling placeholder. If you do know the progress, use a progress bar instead.
using Gtk 4.0;using Adw 1;
Adw.Spinner { width-request: 48; height-request: 48;}import Adw from 'gi://Adw?version=1';import Gtk from 'gi://Gtk?version=4.0';
const spinner = new Adw.Spinner({ widthRequest: 48, heightRequest: 48, halign: Gtk.Align.CENTER, valign: Gtk.Align.CENTER,});<adw-spinner size="48"></adw-spinner><adw:Spinner xmlns="http://schemas.nativescript.org/tns.xsd" xmlns:adw="~/adw" spinning="true" size="48"/>// mount(() => <AdwSpinner />, container) — from '@gjsify/gtk-host/solid'const AdwSpinner = () => ( <adw-spinner widthRequest={48} heightRequest={48} />);<!-- AdwSpinner.vue — mount(AdwSpinner, container) from '@gjsify/gtk-host/vue' --><template> <adw-spinner :width-request="48" :height-request="48" /></template>// createRoot(container).render(<AdwSpinner />) — from '@gjsify/gtk-host/react'const AdwSpinner = () => ( <adw-spinner widthRequest={48} heightRequest={48} />);Status Page
Section titled “Status Page”Fill an empty, error or welcome screen with a centred placeholder: a large symbolic icon, a title and a description. Add a child widget to give the user a way out, such as a button that creates their first document.
using Gtk 4.0;using Adw 1;
Adw.StatusPage { icon-name: "folder-symbolic"; title: _("No Documents"); description: _("Documents you create or open will appear here.");
child: Gtk.Button new_button { label: _("New Document"); halign: center;
styles ["pill", "suggested-action"] };}import { build } from './status-page.blp';
const { new_button } = build();new_button.connect('clicked', () => console.log('Create a document'));<adw-status-page icon-name="folder-symbolic" title="No Documents" description="Documents you create or open will appear here."> <gtk-button id="new_button" label="New Document" halign="center" class="pill suggested-action" slot="child" ></gtk-button></adw-status-page><script type="module"> import '@gjsify/adwaita-web';
const button = document.querySelector('#new_button'); button.addEventListener('click', () => console.log('Create a document'));</script>import { mountSharedTree } from '@gjsify/adwaita-web';import tree from './status-page.blp?shared-tree';
mountSharedTree(tree);
const button = document.querySelector('#new_button');button.addEventListener('click', () => console.log('Create a document'));<adw:StatusPage xmlns="http://schemas.nativescript.org/tns.xsd" xmlns:adw="~/adw" xmlns:gtk="~/gtk" iconName="folder-symbolic" title="No Documents" description="Documents you create or open will appear here."> <adw:StatusPage.child> <gtk:Button id="new_button" label="New Document" halign="center" styleClasses="pill suggested-action" /> </adw:StatusPage.child></adw:StatusPage>import { type Adw, type Gtk } from '@gjsify/adwaita-nativescript';import { Builder } from '@nativescript/core';
const statusPage = Builder.load({ path: '~/views', name: 'status-page' }) as Adw.StatusPage;const button = statusPage.getViewById<Gtk.Button>('new_button')!;button.connect('clicked', () => console.log('Create a document'));import { type Adw, type Gtk } from '@gjsify/adwaita-nativescript';import { build } from '@gjsify/adwaita-nativescript/builder';import tree from './status-page.blp?shared-tree';
const statusPage = build(tree) as Adw.StatusPage;const button = statusPage.getViewById<Gtk.Button>('new_button')!;button.connect('clicked', () => console.log('Create a document'));// mount(() => <AdwStatusPage />, container) — from '@gjsify/gtk-host/solid'const AdwStatusPage = () => ( <adw-status-page iconName="folder-symbolic" title="No Documents" description="Documents you create or open will appear here." > <gtk-button label="New Document" cssClasses={['pill', 'suggested-action']} halign="center" /> </adw-status-page>);<!-- AdwStatusPage.vue — mount(AdwStatusPage, container) from '@gjsify/gtk-host/vue' --><template> <adw-status-page icon-name="folder-symbolic" title="No Documents" description="Documents you create or open will appear here." > <gtk-button label="New Document" :css-classes="['pill', 'suggested-action']" halign="center" /> </adw-status-page></template>// createRoot(container).render(<AdwStatusPage />) — from '@gjsify/gtk-host/react'const AdwStatusPage = () => ( <adw-status-page iconName="folder-symbolic" title="No Documents" description="Documents you create or open will appear here." > <gtk-button label="New Document" cssClasses={['pill', 'suggested-action']} halign="center" /> </adw-status-page>);Window Title
Section titled “Window Title”The title widget a header bar expects: a title with an optional subtitle, already sized and styled for that spot. Leave the subtitle empty and it collapses, so a single-line title stays vertically centred.
using Gtk 4.0;using Adw 1;
Adw.WindowTitle window_title { title: _("Inbox"); subtitle: _("3 unread messages");}import { build } from './window-title.blp';
const { window_title } = build();window_title.subtitle = '4 unread messages';<adw-window-title id="window_title" title="Inbox" subtitle="3 unread messages"></adw-window-title><script type="module"> import '@gjsify/adwaita-web';
const windowTitle = document.querySelector('#window_title'); windowTitle.setAttribute('subtitle', '4 unread messages');</script>import { mountSharedTree } from '@gjsify/adwaita-web';import tree from './window-title.blp?shared-tree';
mountSharedTree(tree);
const windowTitle = document.querySelector('#window_title');windowTitle.setAttribute('subtitle', '4 unread messages');<adw:WindowTitle xmlns="http://schemas.nativescript.org/tns.xsd" xmlns:adw="~/adw" id="window_title" title="Inbox" subtitle="3 unread messages"/>import { type Adw } from '@gjsify/adwaita-nativescript';import { Builder } from '@nativescript/core';
const windowTitle = Builder.load({ path: '~/views', name: 'window-title' }) as Adw.WindowTitle;windowTitle.subtitle = '4 unread messages';import { type Adw } from '@gjsify/adwaita-nativescript';import { build } from '@gjsify/adwaita-nativescript/builder';import tree from './window-title.blp?shared-tree';
const windowTitle = build(tree) as Adw.WindowTitle;windowTitle.subtitle = '4 unread messages';// mount(() => <AdwWindowTitle />, container) — from '@gjsify/gtk-host/solid'const AdwWindowTitle = () => ( <adw-window-title title="Inbox" subtitle="3 unread messages" />);<!-- AdwWindowTitle.vue — mount(AdwWindowTitle, container) from '@gjsify/gtk-host/vue' --><template> <adw-window-title title="Inbox" subtitle="3 unread messages" /></template>// createRoot(container).render(<AdwWindowTitle />) — from '@gjsify/gtk-host/react'const AdwWindowTitle = () => ( <adw-window-title title="Inbox" subtitle="3 unread messages" />);Related
Section titled “Related”- Boxed Lists: the row widgets for settings and detail screens.
- Feedback: toasts and dialogs, for messages that interrupt.
- Layout: the header bar a
WindowTitlegoes into. - Adwaita Storybook: the same widgets in a live component browser you can poke at.