Framework expansion will proceed in this order. All three packages build on the
framework-agnostic tokens and themes provided by @gnome-ui/core.
Priority
Status
Package
Description
1
🚧
@gnome-ui/web-components
Framework-agnostic Custom Elements for GNOME widgets. Foundation: gnome-button, gnome-dialog, gnome-menu, gnome-toast, and gnome-popover; uses semantic HTML and light DOM, exposes standard DOM events, preserves accessibility, and remains compatible with htmx and server-rendered fragments.
2
⬜
@gnome-ui/angular
Angular package built after the Web Components foundation. Provide standalone components, Angular Forms integration where applicable, and reuse @gnome-ui/core tokens plus Web Components rather than duplicating styling and behavior. Blocked on Priority 1.
3
🚧
@gnome-ui/react-native
React Native package targeting iOS/Android/GNOME-mobile shells. No DOM to reuse from Web Components, so components are rebuilt with native primitives (View, Text, Pressable) and StyleSheet; consumes @gnome-ui/core tokens via a generated RN-compatible theme object (JS values, not CSS custom properties) and mirrors the @gnome-ui/react component API/props where the platforms overlap. Independent of Priority 1/2 — can start in parallel since it does not depend on the Web Components foundation.
Breadcrumb path bar for hierarchical navigation; ancestor segments are interactive buttons, current segment is bold non-interactive label; optional per-segment icons — issue #17
Plays the CSS animation embedded in an animated icon from @gnome-ui/icons (Syncing, Recording, Downloading, Connecting); always honors prefers-reduced-motion. Pragmatic web analog to GTK 4.22's GtkSvg native animated-SVG renderer — see @gnome-ui/icons — Icon Library below
GnomeProvider supplies locale, dir, numberFormat, and dateTimeFormat; useNumberFormatter and useDateTimeFormatter apply those defaults across @gnome-ui/react, @gnome-ui/layout, and @gnome-ui/charts
Lightweight components and props derived from the libadwaita 1.9 style-class reference.
These fill gaps between what we already ship and what the full Adwaita toolkit offers.
.spacer separator: invisible flex: 1 filler for Toolbar/HeaderBar to push trailing items to the end — mirrors GtkSeparator.spacer
✅
LinkedGroup
.linked box: children rendered as a single connected unit with no gap and merged borders — canonical GNOME pattern for button groups and segmented inputs
Primary action button with an attached dropdown arrow — mirrors AdwSplitButton; supports suggested and destructive variants
✅
IconButton
Accessible icon-only action button composed from Button, Icon, and optional Tooltip; supports variants, sizes, disabled state, refs, and HTML button attributes — issue #102
✅
Buttonraised variant
Add variant="raised" to Button: explicit raised look for buttons inside flat/toolbar contexts — mirrors .raised style class
✅
Buttonosd modifier
Add osd prop to Button: dark semi-transparent style for controls overlaid on media/images — mirrors .osd style class
✅
CopyButton
Icon button that copies a value to the clipboard via navigator.clipboard, swapping to a checkmark and "Copied!" tooltip/live-region announcement as confirmation; composed from IconButton — no @gnome-ui/platform dependency required
Full-page composition stories that demonstrate real-world use of the component library.
Status
Story
Description
✅
Layout/Dashboard
Full-page app layout: Toolbar with logo + inline SearchBar + action buttons + avatar Popover, collapsible sidebar with SidebarItem + Badge, content area with Card, BoxedList, ExpanderRow, InlineViewSwitcher, StatusPage, and footer Toolbar
✅
Layout/FileManager
GNOME Files (Nautilus)–style file browser: PathBar breadcrumb navigation in toolbar, collapsible Sidebar with Places + Network sections, InlineViewSwitcher for grid/list toggle, folder drill-down, and mobile overlay sidebar — issue #16
✅
Layout/Settings
GNOME Settings–style preferences app: dual-headerbar pattern, 14-category sidebar with per-category icons, sub-page drill-down (Accessibility → Seeing), SwitchRow toggle rows, ActionRow for value navigation, PreferencesGroup sections
Tier 10 — Sidebar Enhancements (identified via Dashboard story)¶
Gaps discovered while building the Dashboard composition story.
Status
Component
Description
✅
Sidebarcollapsed prop
Built-in icon-only (mini/rail) mode: collapsed prop animates width (240 px → 56 px), hides labels/suffixes/section titles, and auto-shows tooltips on hover. SidebarCollapsedContext and useSidebarCollapsed hook exported for custom consumers. Dashboard story updated to use the real Sidebar component.
Framework-agnostic Adwaita symbolic icon definitions. Each icon is a plain IconDefinition object (SVG path data) consumed by the <Icon> component in @gnome-ui/react.
Dedicated npm package that ships the full-page application shell as a first-class primitive.
Consumers can compose any GNOME application layout without pulling in the full @gnome-ui/react bundle.
Status
Component
Description
✅
Layout
Full-page shell with four named zones: topBar (pinned header), sidebar (fixed-width navigation), children (scrollable content), and bottomBar (pinned footer). All zones are optional. Mirrors the composition pattern demonstrated in Layout/Dashboard. Ships as @gnome-ui/layout with its own CSS output, Vitest unit tests, and Storybook story (Layout/Layout).
Pure TypeScript package. Communicates with GNOME via the WebKitGTK window.webkit.messageHandlers bridge or XDG Desktop Portals. Falls back to no-op stubs in standard browsers / test environments.
Status
Module
Description
✅
settings
getSetting/setSetting/onSettingChanged — WebKitGTK via the bridge (request/response correlation for the async Gio.Settings.get_value() round-trip, plus a changed-signal subscription filtered by key). No browser fallback — there is no web equivalent of an app-schema-scoped GSettings store.
✅
notifications
sendNotification/withdrawNotification/onNotificationAction — WebKitGTK via the bridge (Gio.Notification, action buttons), with a real browser Notification fallback (permission flow, single click routed through the same action-event shape as a synthetic 'default' action).
✅
fileChooser
openFile/saveFile/selectFolder — WebKitGTK via the bridge (GtkFileChooserDialog / XDG portal), request/response correlated. No browser fallback — the File System Access API only hands back opaque handles, never real filesystem paths.
✅
colorScheme
getColorScheme/setColorScheme/onColorSchemeChanged — WebKitGTK via the bridge (maps to Adw.StyleManager; set only ever overrides this app's own rendering, never the desktop-wide preference), with a real matchMedia('(prefers-color-scheme: dark)') fallback for get/subscribe in browsers/PWAs. set has no browser fallback — there is no JS API to force the browser's own rendering into a scheme.
✅
window
getWindowState/setMaximized/setFullscreen/minimizeWindow/closeWindow/onWindowStateChanged — WebKitGTK via the bridge (Gtk.Window's maximized/fullscreened/is-active). Browser/PWA fallback for fullscreen (real Fullscreen API), focused (real document.hasFocus()), and close (real window.close(), subject to the browser's own script-only restriction); maximized/minimize have no browser equivalent and either report false or reject.
✅
clipboard
readText/writeText/readImage/writeImage/readFiles/writeFiles — WebKitGTK via the bridge (request/response correlation for the async Gdk.Clipboard reads; images cross the bridge as base64 data: URLs). Real navigator.clipboard fallback for text and images in browsers/PWAs (Blob⇄data: URL conversion keeps both paths returning the same shape). Files have no browser fallback — same constraint as fileChooser: browsers never expose real filesystem paths to page scripts.
✅
portals
callPortal/onPortalSignal — the generic escape hatch behind the other portal-backed modules: an arbitrary { interface, method, args } D-Bus call (request/response correlated, bridging the real portal Request/Response two-step) plus signal subscription filtered by interface+signal. WebKitGTK only — portals are meaningless without a sandboxed host.
React hooks that surface every @gnome-ui/platform module as idiomatic React state.
Status
Hook
Description
✅
useRuntime
Full runtime snapshot: shell, engine, browser, OS
✅
usePlatform
Convenience booleans for the current shell context (isGnomeWebView, isPWA…)
✅
useNativeEvent
Subscribe to an event dispatched by the GJS host via gnome: prefix
✅
useBreakpoint
Reactive isMobile, isTablet, isDesktop flags based on GNOME HIG breakpoints
✅
useSettings
Read and write a GSettings key; re-renders on external changes, optimistic writes, exposes loading/error
✅
useNotification
Returns send/dismiss, scoped to the component's lifetime — notifications sent through it are auto-withdrawn and onAction listeners auto-unsubscribed on unmount
✅
useColorScheme
Returns the resolved "light"/"dark" scheme and a "light"/"dark"/"system" setter, reactive to external changes — talks to Adw.StyleManager via the bridge, distinct from @gnome-ui/react's GnomeProvider-based hook of the same name
✅
useFileChooser
Returns open()/save()/selectFolder() triggers and the resolved path/paths as reactive state, plus loading/error
✅
useClipboard
Returns { value, copy, paste } with reactive clipboard state, plus loading/error — text only; use @gnome-ui/platform's clipboard module directly for images/files
✅
useWindowState
Returns { maximized, fullscreen, focused } and matching actions (setMaximized, setFullscreen, minimize, close), plus loading/error
✅
useHapticFeedback
Trigger haptic feedback via feedbackd (native) or Vibration API (browser) — issue #81
libadwaita 1.10 lets both sidebar widgets host arbitrary widgets before and after their
item list (e.g. an account switcher or prominent title at the top, a help button at the
bottom), and adds extra slots to sections and items.
Status
Item
Description
✅
Sidebarprefix / suffix props
New prefix?: ReactNode and suffix?: ReactNode rendered above and below the scrollable item list (outside the scroll area, so they stay pinned). Must interact correctly with searchable (prefix renders above the search bar), collapsed (slots stay visible but should support icon-only content; consumers can read useSidebarCollapsed), and mode="page" (slots render above/below the boxed list). Mirrors AdwSidebar:prefix / :suffix
✅
ViewSwitcherSidebarprefix / suffix props
Add prefix / suffix as the libadwaita-aligned names for the existing header / footer slots. Keep header / footer as deprecated aliases (JSDoc @deprecated) for one major version; showHeaderSeparator / showFooterSeparator apply to the new names too. Mirrors AdwViewSwitcherSidebar:prefix / :suffix
✅
SidebarSectionsuffix prop
Widget rendered at the end of the section header row (e.g. an "add" IconButton or a count Badge), aligned like PreferencesGroup's headerSuffix. Hidden when the sidebar is collapsed; must not toggle the section when clicked if the section is collapsible (stop propagation)
✅
SidebarItemprefix prop
Arbitrary widget rendered before the label, as an alternative to icon (e.g. an Avatar, a colour dot, a status indicator). When both icon and prefix are set, prefix wins. In collapsed mode the prefix replaces the icon in the rail. Mirrors AdwSidebarItem:prefix
Borderless, background-less trigger that only shows a hover/active background — mirrors the new .flat style class for GtkDropDown
✅
Auto-flat inside toolbars
Dropdown becomes flat automatically when rendered inside HeaderBar, Toolbar or ToolbarView top/bottom bars (the bars set a data-gnome-toolbar attribute that the dropdown CSS keys off; overlays render in portals, so dropdowns inside popovers/dialogs opened from a toolbar keep their regular look), matching libadwaita's automatic flat styling in toolbars
✅
Dropdownraised prop
Opt-out of the automatic toolbar flat style — mirrors the .raised style class override. Also apply to MultiSelectDropdown and FilterableMultiSelectDropdown for consistency
otherApps?: { name: string; summary?: string; icon?: ReactNode; url: string }[] rendered as a boxed list of ActionRows linking to the developer's other applications — the section already exists in AdwAboutDialog but is missing here
✅
AboutDialogotherAppsTitle prop
Custom header for the Other Apps section (default: "Other Apps by {developerName}", falling back to "Other Apps"). The dialog has no localisation mechanism yet — its tab and section labels are English strings. Mirrors the new AdwAboutDialog:other-apps-title property
libadwaita 1.10 renders larger icons with a reduced stroke weight so they look visually
consistent with small icons; AdwStatusPage icons and GtkImage with icon-size: large
now appear thinner, and AdwSpinner respects the -gtk-icon-weight CSS property.
Status
Item
Description
✅
Icon weight token
--gnome-icon-weight in @gnome-ui/core, on the font-weight scale (100–900, default 400) like GTK's -gtk-icon-weight. Components scale stroke widths by var(--gnome-icon-weight) / 400
Blocked:@gnome-ui/icons definitions are filled paths (no strokes), so there is no stroke weight to reduce. GTK 4.23 thins large icons through stroke-based symbolic SVGs. Revisit if the icon set gains stroke-based variants; an SVG feMorphology erode filter was considered and rejected as an approximation
Expose a stable data-overlay attribute (documented as a [data-overlay] selector — CSS module class names are hashed, so they are not a public styling API) on the sidebar pane while it is shown as an overlay on narrow viewports, so consumers can style it differently from the docked sidebar (e.g. shadow, background). Mirrors the unique style class libadwaita 1.10 adds to AdwOverlaySplitView's overlay sidebar
Modifiers are displayed in a fixed order regardless of input order — Hyper → Super → Ctrl → Alt → Shift → Cmd/Meta, i.e. ⌃ → ⌥ → ⇧ → ⌘ — with regular keys after them in their written order. Order taken from get_labels() in libadwaita's adw-shortcut-label.c
✅
macOS key labels
Already covered: ShortcutLabel renders Apple-style symbols (⌃⌥⇧⌘⌫⎋↵) on every platform by default, and symbols={false} shows plain names — no platform detection needed
GNOME 51 adds a system-wide Reduced Motion setting, respected by GTK in GtkSpinner,
GtkStack and GtkRevealer. The web equivalent is prefers-reduced-motion.
Status
Item
Description
✅
prefers-reduced-motion coverage
Already honoured across components (see GNOME 50 Compatibility); Spinner slows its animation instead of stopping it, matching GTK
✅
Stack / Revealer-like transitions audit
Audited NavigationView, Carousel, ViewSwitcher, Expander, Banner, Toast, Drawer and BottomSheet. Fixed two gaps: the Drawer push variant's width animation kept running (its animation-name rule out-ranked the reduced-motion reset), and Banner had no reduced-motion rules. BottomSheet already skips its close animation in JS and Carousel swaps smooth scrolling for instant jumps
✅
Reduced-motion hook
Already available as usePrefersReducedMotion in @gnome-ui/hooks (re-exported by @gnome-ui/react). No host bridge needed: inside a GNOME app, WebKitGTK maps the system animation setting to prefers-reduced-motion. @gnome-ui/react-native keeps its own useReducedMotion from GnomeProvider
Widgets from the GTK4/libadwaita catalog (or well-established HIG-adjacent
patterns) not yet covered by the ~96 components already shipped in
@gnome-ui/react. Scoped to atoms and simple molecules only — no new
organism/page-level compositions. Verified against the current component
tree to avoid duplicating existing functionality (e.g. ColorPicker
already covers Adwaita accent-color swatches via GNOME_PALETTE, so it is
intentionally excluded here).
Discrete level indicator with colour-coded low/high/full offset zones — mirrors GtkLevelBar; distinct from ProgressBar (determinate task progress) and SegmentedBar (proportional category breakdown)
✅
Expander
Standalone disclosure triangle + collapsible content — mirrors GtkExpander; complements ExpanderRow, which only works nested inside a BoxedList row
✅
PasswordField
Standalone masked TextField with a built-in reveal/conceal toggle, for login forms and dialogs outside a settings-row context — reuses the reveal-icon pattern from PasswordEntryRow without requiring ActionRow
✅
Divider
Horizontal rule with an optional centred label (e.g. "OR") — common auth/login-form pattern; distinct from the plain Separator line
✅
RatingStars
Star rating display and interactive input, keyboard-navigable — common HIG-adjacent pattern for review/quality widgets, pairs with Icon's Star/StarOutline
✅
FileTypeIcon
Small icon (optionally with a thumbnail) resolved from a file's MIME type or extension — useful for file-manager-style listings, complementing the existing Layout/FileManager story
✅
Callout
Inline, dismissible admonition box (info/warning/tip variants) for contextual help text within forms and cards — distinct from Banner (persistent, view-level) and Toast (temporary, notification-level)
✅
StepIndicator
Numbered "Step X of Y" progress indicator for onboarding/wizard flows — complements the existing CarouselIndicatorDots/CarouselIndicatorLines with a labelled, linear alternative
✅
RangeSlider
Dual-thumb slider for selecting a min/max range — Slider is single-thumb only; needed for range filters (price, date range, etc.)
✅
TextTruncate
Single/multi-line text truncation with an automatic tooltip revealing the full content on overflow — mirrors GtkLabel's ellipsize property
✅
Kbd
Standalone single key-cap (e.g. Enter, Esc) for inline instructional text ("press Enter to continue") — complements ShortcutLabel, which only renders full +-delimited combos and doesn't expose its key-cap rendering separately
✅
Highlight
Inline <mark>-based text highlighting for matched search terms — pairs with SearchBar's suggestion list and any filterable list/table
✅
VisuallyHidden
Reusable "sr-only" utility — visually hides content while keeping it in the accessibility tree; extracts the recipe currently duplicated inline inside CopyButton's live-region announcement
✅
Overlay
Standalone backdrop/scrim layer with fade transition and click-to-dismiss — extracts the near-identical backdrop implementation currently duplicated across Modal, Dialog, and BottomSheet
Month-grid date display with keyboard navigation — mirrors GtkCalendar; usable standalone or as the panel inside DatePicker
✅
DatePicker
TextField trigger + Popover-anchored Calendar — mirrors the GtkCalendar + GtkPopover composition pattern used for date entry across GNOME apps. showTime adds TimePicker's hour/minute columns under the calendar, turning the emitted Date into a point in time: the day click then keeps the popover open and a Done button finishes the selection
✅
TimePicker
Hour/minute selection built from paired SpinButtons in a Popover, with 12/24-hour format support. The columns live in an internal TimeFields, shared with DatePicker/DateRangePicker's showTime footer
✅
CalendarRange
Start/end date-range selection sharing Calendar's grid engine (roving tabindex, month/year drill-down, min/max). Emits through onChangeonly once both ends have a value — a first click sets the anchor and paints a live preview band up to the hovered (or keyboard-focused) day, and the second click commits the range; picking backwards reorders the pair instead of failing. visibleMonths (default 1) renders N side-by-side month panels paged as one unit, so a range spanning months is picked without navigating. Adds minRange/maxRange day limits on top of min/max, aria-selected on every in-range cell plus start/in-range/end suffixes in the day labels, and Esc to cancel an in-progress range (after leaving the drill-down)
✅
DateRangePicker
Popover-anchored CalendarRange behind a DatePicker-style trigger reading "1 Aug – 15 Aug 2026". Defaults to visibleMonths={2}, closes only on a complete range, and offers an optional preset sidebar (last 7 days, this month, last quarter) — the composition counterpart of DatePicker, mirroring GtkCalendar + GtkPopover. showTime gives each end its own hour/minute columns plus a Done button, keeping start ≤ end when both land on the same day
✅
FontPicker
Button that opens a family/size/weight chooser — mirrors GtkFontDialogButton
✅
EmojiPicker
Searchable emoji grid in a Popover — mirrors GtkEmojiChooser
✅
TagInput
Type-to-add multi-value input rendering entries as removable Chips in a WrapBox — WrapBox/Chip currently only support static/pre-populated display, not interactive entry
✅
OtpInput
Segmented PIN/verification-code input (one cell per digit, auto-advance, paste support) — common auth pattern, pairs naturally with PasswordEntryRow/PasswordField
✅
CopyField
Read-only TextField/EntryRow with a built-in trailing CopyButton — for displaying copyable values (API keys, tokens, IDs) outside the CveIdentifier/CweIdentifier-style specialised components
✅
ChoiceCardGroup
Card-based single-choice selector (large selectable cards instead of radio buttons) — mirrors the pattern used in GNOME Initial Setup / welcome flows
✅
FileDropZone
Drag-and-drop file upload target with hover/active states, falling back to a GtkFileDialog-style click-to-browse trigger
✅
MultiSelectDropdown
Checkbox-list variant of Dropdown for selecting multiple values from a single trigger — Dropdown/ComboRow are currently single-select only
✅
CodeBlock
Static monospace code/config snippet display with optional line numbers and a CopyButton trailing action — distinct from TerminalView (live scrolling terminal output)
✅
WidgetManager
Card that manages a controlled collection of widgets picked from a catalog, each rendering its own content via render(); edit-mode trigger opens a staged add/remove picker (Modal/BottomSheet/Drawer, configurable) applied only on confirm
✅
FieldGroup
Generic form-field grouping with a shared label, help text, and error message, for arbitrary fields outside a BoxedList — PreferencesGroup is scoped specifically to wrapping settings rows
✅
Portal
Reusable createPortal wrapper (mount target, SSR-safe no-op) — extracts the ad-hoc portal logic currently duplicated independently across 13 components (Dialog, Modal, Popover, Tooltip, BottomSheet, etc.)
✅
CoachMark
Onboarding feature-discovery pattern: spotlights a target element and anchors a callout bubble (title, description, actions) beside it, with a CoachMarkTour orchestrator for sequential steps (Next/Back/Skip/Done + "X of N"). Pragmatic web-app pattern beyond GNOME HIG, built on Button/Portal and a viewport-aware flip positioner