Skip to content

Layout

These four containers give an Adwaita window its shape. Use a clamp to hold content at a readable width, a header bar for the top chrome, a toolbar view to frame content between fixed bars, and a wrap box to let a run of children flow onto new lines. None of them hold data: they position what you put inside and adapt as the space around them changes.

Each block below is a stack of windows. The first one RUNS the widget: the preview is the live browser port, so it follows the light and dark toggle the way a real window would, and the HTML tab beside it holds the very markup that paints it — one import '@gjsify/adwaita-web' registers every element in it. Then comes one window per kind of implementation, for every kind that can express this widget. Two of them name a file pair — Native TypeScript, the @girs program that runs unchanged on GJS, Node, Bun and Deno, with the matching Blueprint declaration beside it, and NativeScript, the port that runs on a phone, which splits the same way: an XML template holds the tree and the TypeScript beside it is the loader.

A NativeScript template reaches these widgets through its xmlns, and the namespace is a MODULE: NativeScript loads it and reads the element name off its exports, so xmlns:adw="~/adwaita" needs an app-local app/adwaita.ts that re-exports what the markup names. A bare package specifier there does not resolve. Every XML tab in this gallery was inflated on an Android device before it was written down — adwaita-gallery-nativescript loads each template through NativeScript’s own Builder and asserts the view tree it built, which is the only way to tell a template that renders from one that renders something else: an attribute arrives as a string, and a child a widget has no slot for is added to the layout anyway.

The third, UI frameworks, is on all four as well: each shows the same widget written in Solid, Vue and React through @gjsify/gtk-host, with a @gjsify/react-native tab beside them. The wrap box only recently joined them. @gjsify/gtk-host had no child policy for AdwWrapBox, so a child placed in one was refused by name — deliberately, because a renderer that guessed the policy would call add, append or set_child and be wrong at exit 0. The policy is now curated against libadwaita, so the block has a tree like the other three. That window renders a GTK window, not a phone screen — which is why it is not the NativeScript one, and the React Native page says what does and does not exist there.

Two of those three style with className, and both configure their token scales on the way in. That line is not decoration: the values behind a class name come from the project, the layer’s default scales are deliberately small (spacing holds 0 and px alone), and a class naming an undeclared token throws out of the render — leaving an empty window at exit 0 with nothing in the log. The header bar’s snippet names no class and needs no such line.

Libadwaita is the reference here, typed by @girs/adw-1. @gjsify/adwaita-web and @gjsify/adwaita-nativescript follow its naming, and where one of them differs, its own window says so.

Wrap long-form content in a clamp so it stops growing at a maximum width and stays centred in anything wider. As the window shrinks, the clamp starts tightening the child once it passes the tightening threshold. That keeps text at a comfortable measure on a large display without wasting room on a small one.

Adw.Clamp
HTML
<adw-clamp maximum-size="400">
<adw-card style="width: 100%; box-sizing: border-box; padding: 18px;">
This content is clamped: it stops growing past the maximum size and stays centred.
</adw-card>
</adw-clamp>
<adw-clamp> observes these in the browser port. Changing one re-renders the element. The second column is what the preview one tab to the left writes, read across every copy of the element in it. The other windows name their own properties.
Attribute In this preview
maximum-size 400
tightening-threshold not used
Native TypeScript
TypeScript
import Adw from 'gi://Adw?version=1';
import Gtk from 'gi://Gtk?version=4.0';
const inner = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL, widthRequest: 520 });
inner.add_css_class('card');
const label = new Gtk.Label({
label: 'This content is clamped: it stops growing past the maximum size and stays centred.',
wrap: true,
xalign: 0,
hexpand: true,
marginTop: 18,
marginBottom: 18,
marginStart: 18,
marginEnd: 18,
});
inner.append(label);
const clamp = new Adw.Clamp({
maximumSize: 400,
tighteningThreshold: 300,
child: inner,
});
Code
using Gtk 4.0;
using Adw 1;
Adw.Clamp {
maximum-size: 400;
child: Gtk.Label {
label: _("This content is clamped: it stops growing past the maximum size and stays centred.");
wrap: true;
styles ["card"]
};
}
UI frameworks
TypeScript
import { Text, View } from 'react-native';
import { configureStyle } from '@gjsify/react-native';
// The class VALUES are the project's. The layer's default spacing scale holds `0`
// and `px` only, so a class naming a token you have not declared throws out of the
// render — the window is then empty at exit 0, with no GTK diagnostic.
configureStyle({ tokens: { spacing: { m: '12px' }, fontSize: { title: '18px' } } });
export function ReadingColumn() {
return (
<adw-clamp maximumSize={400} tighteningThreshold={300}>
{/* One child: `AdwClamp`'s policy is `set_child`, measured. */}
<View className="flex-col gap-m p-m">
<Text className="text-title">Clamped</Text>
<Text>It stops growing past the maximum size and stays centred.</Text>
</View>
</adw-clamp>
);
}
TypeScript
// mount(() => <AdwClamp />, container) — from '@gjsify/gtk-host/solid'
const AdwClamp = () => (
<adw-clamp
maximumSize={400}
tighteningThreshold={300}
>
<gtk-label
label="This content is clamped: it stops growing past the maximum size and stays centred."
wrap
xalign={0}
cssClasses={['card']}
/>
</adw-clamp>
);
Vue
<!-- AdwClamp.vue — mount(AdwClamp, container) from '@gjsify/gtk-host/vue' -->
<template>
<adw-clamp
:maximum-size="400"
:tightening-threshold="300"
>
<gtk-label
label="This content is clamped: it stops growing past the maximum size and stays centred."
:wrap="true"
:xalign="0"
:css-classes="['card']"
/>
</adw-clamp>
</template>
TypeScript
// createRoot(container).render(<AdwClamp />) — from '@gjsify/gtk-host/react'
const AdwClamp = () => (
<adw-clamp
maximumSize={400}
tighteningThreshold={300}
>
<gtk-label
label="This content is clamped: it stops growing past the maximum size and stays centred."
wrap
xalign={0}
cssClasses={['card']}
/>
</adw-clamp>
);
NativeScript
TypeScript
// app/adwaita.ts — the module the template's `xmlns:adw="~/adwaita"` resolves to.
// NativeScript reads `AdwClamp` off THIS module's exports; a bare package
// specifier in the xmlns does not resolve.
export { AdwClamp } from '@gjsify/adwaita-nativescript';
// app/views/clamp.ts — the loader. The template holds the tree; this holds the
// wiring, and here there is none beyond mounting it.
import { Builder } from '@nativescript/core';
export const clamp = Builder.load({ path: '~/views', name: 'clamp' });
XML
<adw:AdwClamp
xmlns="http://schemas.nativescript.org/tns.xsd"
xmlns:adw="~/adwaita"
maximumSize="400"
tighteningThreshold="300"
>
<Label
class="card"
textWrap="true"
text="This content is clamped: it stops growing past the maximum size and stays centred."
/>
</adw:AdwClamp>

The standard top chrome of an Adwaita window. Put a title widget in the centre, usually a window title with a subtitle, and pack controls to the start and end edges: navigation on the leading side, menu and action buttons on the trailing one.

Adw.HeaderBar
HTML
<adw-header-bar style="width: 100%;">
<gtk-button slot="start" icon="go-previous" flat></gtk-button>
<adw-window-title slot="center" title="Text Editor" subtitle="notes.md"></adw-window-title>
<gtk-button slot="end" icon="open-menu" flat></gtk-button>
</adw-header-bar>
<adw-header-bar> observes these in the browser port. Changing one re-renders the element. The second column is what the preview one tab to the left writes, read across every copy of the element in it. The other windows name their own properties.
Attribute In this preview
title not used
subtitle not used
Native TypeScript
TypeScript
import Adw from 'gi://Adw?version=1';
import Gtk from 'gi://Gtk?version=4.0';
import Gio from 'gi://Gio?version=2.0';
const title = new Adw.WindowTitle({ title: 'Text Editor', subtitle: 'notes.md' });
const headerBar = new Adw.HeaderBar({ titleWidget: title, showTitle: true });
const backButton = new Gtk.Button({ iconName: 'go-previous-symbolic' });
backButton.add_css_class('flat');
headerBar.pack_start(backButton);
const menu = new Gio.Menu();
menu.append('New Window', 'app.new-window');
menu.append('Preferences', 'app.preferences');
menu.append('About', 'app.about');
const menuButton = new Gtk.MenuButton({ iconName: 'open-menu-symbolic', menuModel: menu });
menuButton.add_css_class('flat');
headerBar.pack_end(menuButton);
Code
using Gtk 4.0;
using Adw 1;
Adw.HeaderBar {
title-widget: Adw.WindowTitle {
title: _("Text Editor");
subtitle: "notes.md";
};
[start]
Gtk.Button {
icon-name: "go-previous-symbolic";
styles ["flat"]
}
[end]
Gtk.MenuButton {
icon-name: "open-menu-symbolic";
styles ["flat"]
}
}
UI frameworks
TypeScript
import { Pressable, Text } from 'react-native';
export function EditorHeader({ onBack }: { onBack: () => void }) {
return (
<adw-header-bar>
{/* A header bar's default slot is `start`, and a React Native
primitive can only go in a default slot: `slot` is not one of
its props, and an unlisted prop is refused by name. */}
<Pressable onPress={onBack}>
<Text>Back</Text>
</Pressable>
<adw-window-title slot="title" title="Text Editor" subtitle="notes.md" />
<gtk-button slot="end" iconName="open-menu-symbolic" cssClasses={['flat']} />
</adw-header-bar>
);
}
TypeScript
// mount(() => <AdwHeaderBar />, container) — from '@gjsify/gtk-host/solid'
const AdwHeaderBar = () => (
<adw-header-bar>
<gtk-button
slot="start"
iconName="go-previous-symbolic"
cssClasses={['flat']}
/>
<adw-window-title
slot="title"
title="Text Editor"
subtitle="notes.md"
/>
<gtk-menu-button
slot="end"
iconName="open-menu-symbolic"
cssClasses={['flat']}
/>
</adw-header-bar>
);
Vue
<!-- AdwHeaderBar.vue — mount(AdwHeaderBar, container) from '@gjsify/gtk-host/vue' -->
<template>
<adw-header-bar>
<gtk-button
slot="start"
icon-name="go-previous-symbolic"
:css-classes="['flat']"
/>
<adw-window-title
slot="title"
title="Text Editor"
subtitle="notes.md"
/>
<gtk-menu-button
slot="end"
icon-name="open-menu-symbolic"
:css-classes="['flat']"
/>
</adw-header-bar>
</template>
TypeScript
// createRoot(container).render(<AdwHeaderBar />) — from '@gjsify/gtk-host/react'
const AdwHeaderBar = () => (
<adw-header-bar>
<gtk-button
slot="start"
iconName="go-previous-symbolic"
cssClasses={['flat']}
/>
<adw-window-title
slot="title"
title="Text Editor"
subtitle="notes.md"
/>
<gtk-menu-button
slot="end"
iconName="open-menu-symbolic"
cssClasses={['flat']}
/>
</adw-header-bar>
);
NativeScript
TypeScript
// app/adwaita.ts — the module the template's `xmlns:adw="~/adwaita"` resolves to.
export { GtkButton, AdwHeaderBar, AdwWindowTitle } from '@gjsify/adwaita-nativescript';
// app/views/header-bar.ts — the loader.
import { Builder } from '@nativescript/core';
export const headerBar = Builder.load({ path: '~/views', name: 'header-bar' });
XML
<adw:AdwHeaderBar
xmlns="http://schemas.nativescript.org/tns.xsd"
xmlns:adw="~/adwaita"
>
<adw:AdwHeaderBar.startBox>
<adw:GtkButton
text=""
variant="flat"
/>
</adw:AdwHeaderBar.startBox>
<adw:AdwHeaderBar.titleWidget>
<adw:AdwWindowTitle
title="Text Editor"
subtitle="notes.md"
/>
</adw:AdwHeaderBar.titleWidget>
<adw:AdwHeaderBar.endBox>
<adw:GtkButton
text=""
variant="flat"
/>
</adw:AdwHeaderBar.endBox>
</adw:AdwHeaderBar>

Frame your content between a top bar and a bottom bar, typically a header bar above and an action bar below. Each set of bars reveals and hides independently, and the content scrolls underneath, so the chrome stays put while the view moves.

Adw.ToolbarView
HTML
<adw-toolbar-view style="flex: 1;">
<adw-header-bar slot="top">
<adw-window-title slot="center" title="Documents" subtitle="12 items"></adw-window-title>
</adw-header-bar>
<adw-status-page icon="folder-documents" title="Your library" description="Content sits between the toolbars and scrolls independently of them."></adw-status-page>
<div slot="bottom" class="toolbar" style="border-top: 1px solid var(--separator-color, rgba(0, 0, 0, 0.15));">
<gtk-button icon="list-add" flat tooltip="Add"></gtk-button>
<gtk-button icon="list-remove" flat tooltip="Remove"></gtk-button>
<span style="flex: 1 1 auto; text-align: center;">Selection: none</span>
<gtk-button icon="send-to" flat tooltip="Share"></gtk-button>
</div>
</adw-toolbar-view>
<adw-toolbar-view> observes these in the browser port. Changing one re-renders the element. The second column is what the preview one tab to the left writes, read across every copy of the element in it. The other windows name their own properties.
Attribute In this preview
top-bar-style not used
bottom-bar-style not used
extend-content-to-top-edge not used
extend-content-to-bottom-edge not used
Native TypeScript
TypeScript
import Adw from 'gi://Adw?version=1';
import Gtk from 'gi://Gtk?version=4.0';
const view = new Adw.ToolbarView();
const header = new Adw.HeaderBar({
titleWidget: new Adw.WindowTitle({ title: 'Documents', subtitle: '12 items' }),
});
view.add_top_bar(header);
const content = new Adw.StatusPage({
iconName: 'folder-documents-symbolic',
title: 'Your library',
description: 'Content sits between the toolbars and scrolls independently of them.',
});
view.content = content;
const bottomBar = new Gtk.ActionBar();
const addButton = new Gtk.Button({ iconName: 'list-add-symbolic' });
addButton.add_css_class('flat');
bottomBar.pack_start(addButton);
const removeButton = new Gtk.Button({ iconName: 'list-remove-symbolic' });
removeButton.add_css_class('flat');
bottomBar.pack_start(removeButton);
bottomBar.set_center_widget(new Gtk.Label({ label: 'Selection: none' }));
const shareButton = new Gtk.Button({ iconName: 'send-to-symbolic' });
shareButton.add_css_class('flat');
bottomBar.pack_end(shareButton);
view.add_bottom_bar(bottomBar);
Code
using Gtk 4.0;
using Adw 1;
Adw.ToolbarView {
[top]
Adw.HeaderBar {
title-widget: Adw.WindowTitle {
title: _("Documents");
subtitle: _("12 items");
};
}
content: Adw.StatusPage {
icon-name: "folder-documents-symbolic";
title: _("Your library");
description: _("Content sits between the toolbars and scrolls independently of them.");
};
[bottom]
Gtk.ActionBar {
[start]
Gtk.Button {
icon-name: "list-add-symbolic";
tooltip-text: _("Add");
styles ["flat"]
}
[start]
Gtk.Button {
icon-name: "list-remove-symbolic";
tooltip-text: _("Remove");
styles ["flat"]
}
[center]
Gtk.Label {
label: _("Selection: none");
}
[end]
Gtk.Button {
icon-name: "send-to-symbolic";
tooltip-text: _("Share");
styles ["flat"]
}
}
}
UI frameworks
TypeScript
import { Pressable, Text, View } from 'react-native';
import { configureStyle } from '@gjsify/react-native';
// Before the first render, and not optional: `gap-s`, `p-xl`, `text-title` and
// `text-caption` are this project's names. Without the scales the first one
// throws, React unmounts the tree, and the window is empty at exit 0.
configureStyle({
tokens: { spacing: { s: '8px', xl: '24px' }, fontSize: { caption: '11px', title: '18px' } },
});
export function Documents({ onAdd }: { onAdd: () => void }) {
return (
<adw-toolbar-view>
<adw-header-bar slot="top">
<adw-window-title slot="title" title="Documents" subtitle="12 items" />
</adw-header-bar>
{/* The default slot is the content, and the content is React Native's. */}
<View className="flex-col items-center justify-center gap-s p-xl">
<Text className="text-title">Your library</Text>
<Text className="text-caption">Content sits between the toolbars.</Text>
</View>
{/* A box with the `toolbar` style class, not a Gtk.ActionBar: the
action bar is in gtk-host's generated table, which knows the tag
but not how it adopts a child, so it refuses one. */}
<gtk-box slot="bottom" cssClasses={['toolbar']}>
<Pressable onPress={onAdd}>
<Text>Add</Text>
</Pressable>
</gtk-box>
</adw-toolbar-view>
);
}
TypeScript
// mount(() => <AdwToolbarView />, container) — from '@gjsify/gtk-host/solid'
const AdwToolbarView = () => (
<adw-toolbar-view>
<adw-header-bar slot="top">
<adw-window-title
slot="title"
title="Documents"
subtitle="12 items"
/>
</adw-header-bar>
<adw-status-page
slot="content"
iconName="folder-documents-symbolic"
title="Your library"
description="Content sits between the toolbars and scrolls independently of them."
/>
<gtk-box
slot="bottom"
cssClasses={['toolbar']}
spacing={6}
>
<gtk-button
iconName="list-add-symbolic"
cssClasses={['flat']}
/>
<gtk-button
iconName="list-remove-symbolic"
cssClasses={['flat']}
/>
<gtk-label
label="Selection: none"
hexpand
/>
<gtk-button
iconName="send-to-symbolic"
cssClasses={['flat']}
/>
</gtk-box>
</adw-toolbar-view>
);
Vue
<!-- AdwToolbarView.vue — mount(AdwToolbarView, container) from '@gjsify/gtk-host/vue' -->
<template>
<adw-toolbar-view>
<adw-header-bar slot="top">
<adw-window-title
slot="title"
title="Documents"
subtitle="12 items"
/>
</adw-header-bar>
<adw-status-page
slot="content"
icon-name="folder-documents-symbolic"
title="Your library"
description="Content sits between the toolbars and scrolls independently of them."
/>
<gtk-box
slot="bottom"
:css-classes="['toolbar']"
:spacing="6"
>
<gtk-button
icon-name="list-add-symbolic"
:css-classes="['flat']"
/>
<gtk-button
icon-name="list-remove-symbolic"
:css-classes="['flat']"
/>
<gtk-label
label="Selection: none"
:hexpand="true"
/>
<gtk-button
icon-name="send-to-symbolic"
:css-classes="['flat']"
/>
</gtk-box>
</adw-toolbar-view>
</template>
TypeScript
// createRoot(container).render(<AdwToolbarView />) — from '@gjsify/gtk-host/react'
const AdwToolbarView = () => (
<adw-toolbar-view>
<adw-header-bar slot="top">
<adw-window-title
slot="title"
title="Documents"
subtitle="12 items"
/>
</adw-header-bar>
<adw-status-page
slot="content"
iconName="folder-documents-symbolic"
title="Your library"
description="Content sits between the toolbars and scrolls independently of them."
/>
<gtk-box
slot="bottom"
cssClasses={['toolbar']}
spacing={6}
>
<gtk-button
iconName="list-add-symbolic"
cssClasses={['flat']}
/>
<gtk-button
iconName="list-remove-symbolic"
cssClasses={['flat']}
/>
<gtk-label
label="Selection: none"
hexpand
/>
<gtk-button
iconName="send-to-symbolic"
cssClasses={['flat']}
/>
</gtk-box>
</adw-toolbar-view>
);
NativeScript
TypeScript
// app/adwaita.ts — the module the template's `xmlns:adw="~/adwaita"` resolves to.
export {
AdwHeaderBar,
AdwImageButton,
AdwStatusPage,
AdwToolbarView,
AdwWindowTitle,
} from '@gjsify/adwaita-nativescript';
// app/views/toolbar-view.ts — the loader, and the three things markup cannot say.
import { Builder } from '@nativescript/core';
import type { AdwImageButton, AdwStatusPage } from '@gjsify/adwaita-nativescript';
import { listAddSymbolic, sendToSymbolic } from '@gjsify/adwaita-icons/actions';
import { folderSymbolic } from '@gjsify/adwaita-icons/places';
export const view = Builder.load({ path: '~/views', name: 'toolbar-view' });
// An icon here is the SVG SOURCE, not a theme name — NativeScript's Image
// decodes no SVG, so the widget rasterises the markup itself. An XML attribute
// is a string, so the template declares the buttons and gives them ids, and the
// loader hands each one its glyph.
view.getViewById<AdwStatusPage>('library').iconName = folderSymbolic;
view.getViewById<AdwImageButton>('add').iconName = listAddSymbolic;
view.getViewById<AdwImageButton>('share').iconName = sendToSymbolic;
XML
<adw:AdwToolbarView
xmlns="http://schemas.nativescript.org/tns.xsd"
xmlns:adw="~/adwaita"
>
<adw:AdwToolbarView.topBar>
<adw:AdwHeaderBar
title="Documents"
subtitle="12 items"
/>
</adw:AdwToolbarView.topBar>
<adw:AdwToolbarView.content>
<adw:AdwStatusPage
id="library"
title="Your library"
description="Content sits between the toolbars and scrolls independently of them."
/>
</adw:AdwToolbarView.content>
<adw:AdwToolbarView.bottomBar>
<adw:AdwHeaderBar>
<adw:AdwHeaderBar.titleWidget>
<adw:AdwWindowTitle title="Selection: none" />
</adw:AdwHeaderBar.titleWidget>
</adw:AdwHeaderBar>
</adw:AdwToolbarView.bottomBar>
</adw:AdwToolbarView>

Lay children out in a horizontal line and let them wrap onto new lines when they run out of room. This is what you want for a variable-length run of tags, chips or filter pills. Child spacing sets the gap between items on a line, line spacing the gap between the lines.

Adw.WrapBox
HTML
<adw-wrap-box child-spacing="8" line-spacing="8">
<gtk-button label="Design" pill></gtk-button>
<gtk-button label="Adwaita" pill></gtk-button>
<gtk-button label="GNOME" pill></gtk-button>
<gtk-button label="GTK" pill></gtk-button>
<gtk-button label="Typescript" pill></gtk-button>
<gtk-button label="Storybook" pill></gtk-button>
<gtk-button label="Wrapping" pill></gtk-button>
<gtk-button label="Layout" pill></gtk-button>
</adw-wrap-box>
<adw-wrap-box> observes these in the browser port. Changing one re-renders the element. The second column is what the preview one tab to the left writes, read across every copy of the element in it. The other windows name their own properties.
Attribute In this preview
child-spacing 8
child-spacing-unit not used
pack-direction not used
align not used
justify not used
justify-last-line not used
line-spacing 8
line-spacing-unit not used
line-homogeneous not used
natural-line-length not used
natural-line-length-unit not used
wrap-reverse not used
wrap-policy not used
orientation not used
Native TypeScript
TypeScript
import Adw from 'gi://Adw?version=1';
import Gtk from 'gi://Gtk?version=4.0';
const wrap = new Adw.WrapBox({ childSpacing: 8, lineSpacing: 8 });
const tags = ['Design', 'Adwaita', 'GNOME', 'GTK', 'Typescript', 'Storybook', 'Wrapping', 'Layout'];
for (const tag of tags) {
const chip = new Gtk.Button({ label: tag });
chip.add_css_class('pill');
wrap.append(chip);
}
Code
using Gtk 4.0;
using Adw 1;
Adw.WrapBox {
child-spacing: 8;
line-spacing: 8;
Gtk.Button {
label: _("Design");
styles ["pill"]
}
Gtk.Button {
label: _("Adwaita");
styles ["pill"]
}
Gtk.Button {
label: _("GNOME");
styles ["pill"]
}
Gtk.Button {
label: _("GTK");
styles ["pill"]
}
Gtk.Button {
label: _("Typescript");
styles ["pill"]
}
Gtk.Button {
label: _("Storybook");
styles ["pill"]
}
Gtk.Button {
label: _("Wrapping");
styles ["pill"]
}
Gtk.Button {
label: _("Layout");
styles ["pill"]
}
}
UI frameworks
TypeScript
// mount(() => <AdwWrapBox />, container) — from '@gjsify/gtk-host/solid'
const AdwWrapBox = () => (
<adw-wrap-box
childSpacing={8}
lineSpacing={8}
>
<gtk-button
label="Design"
cssClasses={['pill']}
/>
<gtk-button
label="Adwaita"
cssClasses={['pill']}
/>
<gtk-button
label="GNOME"
cssClasses={['pill']}
/>
<gtk-button
label="GTK"
cssClasses={['pill']}
/>
<gtk-button
label="Typescript"
cssClasses={['pill']}
/>
<gtk-button
label="Storybook"
cssClasses={['pill']}
/>
<gtk-button
label="Wrapping"
cssClasses={['pill']}
/>
<gtk-button
label="Layout"
cssClasses={['pill']}
/>
</adw-wrap-box>
);
Vue
<!-- AdwWrapBox.vue — mount(AdwWrapBox, container) from '@gjsify/gtk-host/vue' -->
<template>
<adw-wrap-box
:child-spacing="8"
:line-spacing="8"
>
<gtk-button
label="Design"
:css-classes="['pill']"
/>
<gtk-button
label="Adwaita"
:css-classes="['pill']"
/>
<gtk-button
label="GNOME"
:css-classes="['pill']"
/>
<gtk-button
label="GTK"
:css-classes="['pill']"
/>
<gtk-button
label="Typescript"
:css-classes="['pill']"
/>
<gtk-button
label="Storybook"
:css-classes="['pill']"
/>
<gtk-button
label="Wrapping"
:css-classes="['pill']"
/>
<gtk-button
label="Layout"
:css-classes="['pill']"
/>
</adw-wrap-box>
</template>
TypeScript
// createRoot(container).render(<AdwWrapBox />) — from '@gjsify/gtk-host/react'
const AdwWrapBox = () => (
<adw-wrap-box
childSpacing={8}
lineSpacing={8}
>
<gtk-button
label="Design"
cssClasses={['pill']}
/>
<gtk-button
label="Adwaita"
cssClasses={['pill']}
/>
<gtk-button
label="GNOME"
cssClasses={['pill']}
/>
<gtk-button
label="GTK"
cssClasses={['pill']}
/>
<gtk-button
label="Typescript"
cssClasses={['pill']}
/>
<gtk-button
label="Storybook"
cssClasses={['pill']}
/>
<gtk-button
label="Wrapping"
cssClasses={['pill']}
/>
<gtk-button
label="Layout"
cssClasses={['pill']}
/>
</adw-wrap-box>
);
NativeScript
TypeScript
// app/adwaita.ts — the module the template's `xmlns:adw="~/adwaita"` resolves to.
export { GtkButton, AdwWrapBox } from '@gjsify/adwaita-nativescript';
// app/views/wrap-box.ts — the loader. A fixed run of chips is a tree, so it is
// the template's; build the children here instead when the run comes from data.
import { Builder } from '@nativescript/core';
export const wrapBox = Builder.load({ path: '~/views', name: 'wrap-box' });
XML
<adw:AdwWrapBox
xmlns="http://schemas.nativescript.org/tns.xsd"
xmlns:adw="~/adwaita"
childSpacing="8"
lineSpacing="8"
>
<adw:GtkButton
text="Design"
variant="pill"
/>
<adw:GtkButton
text="Adwaita"
variant="pill"
/>
<adw:GtkButton
text="GNOME"
variant="pill"
/>
<adw:GtkButton
text="GTK"
variant="pill"
/>
<adw:GtkButton
text="TypeScript"
variant="pill"
/>
<adw:GtkButton
text="Storybook"
variant="pill"
/>
</adw:AdwWrapBox>