Features
By default Marble comes with a prebuild desktop shell.
IMPORTANT
The workspace and client list indicator currently only supports Hyprland. If you want to use it on another compositor you will have to configure it and disable Hyprland specific components.
Components
Marble is, at its core, a component toolkit. It offers pre-built, pre-styled components that you can use to craft personalized layouts and themes.
IMPORTANT
Marble is not yet compatible with AGS
, but only the provided marble
executable.
Example primitive components:
Avatar
tsx<Avatar r={17} size={74} fallbackIcon="folder-user" filePath={`/var/lib/AccountsService/icons/${GLib.get_user_name()}`} />
Bar
tsx<Bar position="top" start={<MyBarStartComponent />} center={<></>} end={<></>} />
There is also a
ScreenCorner
component which pairs nicely with bars.tsx<ScreenCorners position="top" r={13} />
Box
tsx<Box gap={5} mx={5} my={12} p={5} vertical> Box content </Box>
Button
tsx<Button flat r={5} px={3} py={5} onPrimaryClick={() => print("clicked")}> Button content </Button>
CalendarGrid
tsxconst [date, setDate] = createState({ year: 2025, month: 6 }) return ( <CalendarGrid date={date}> {(date) => ( <Button onPrimaryClick={() => setDate(date.get())}> <Text>{date((d) => `${d.day}`)}</Text> </Button> )} </CalendarGrid> )
Icon
tsx<Icon icon="system-search" fallback="image-missing" />
Modal
tsx<Modal p={15} r={19} open={open} onClose={setOpen}> Modal content </Modal>
TIP
Not every component is listed here. There is a new work in progress website which will have a more comprehensive list.
Indicator components:
BatteryIndicator
BatteryLabel
BatteryLevelBar
BluetoothIndicator
ClockLabel
HyprlandClients
HyprlandWorkspaces
MediaIndicator
MicrophoneIndicator
NetworkIndicator
NotificationsIndicator
PowerProfilesIndicator
SinkTypeIndicator
SpeakerIndicator
TrayItems
UptimeLabel
More complex widgets:
Notifications
tsx<NotificationPopups anchor="top-right" timeout={3000}> {() => ( <NotificationRoot hideOnHover> <Box hexpand vertical> <Box> <NotificationAppIcon /> <NotificationAppName opacity={0.8} /> <NotificationTimestamp opacity={0.7} /> <NotificationDismissButton flat color="error"> <Icon icon="window-close" /> </NotificationDismissButton> </Box> <Separator /> <Box> <NotificationImage size={86} /> <Box vertical> <NotificationSummary /> <NotificationBody /> </Box> </Box> </Box> </NotificationRoot> )} </NotificationPopups>
MediaPlayer
tsx<MprisList> {() => ( <Box> <MprisCoverArt /> <Box vertical> <Box> <MprisTitle /> <MprisArtist /> <MprisRaiseButton flat> <MprisBrandIcon /> </MprisRaiseButton> </Box> <MprisPositionSlider /> <Gtk.CenterBox> <MprisPositionLabel $type="start" /> <Box $type="center"> <MprisPrevButton flat> <Icon icon="media-skip-backward" /> </MprisPrevButton> <MprisPlayPauseButton flat> <MprisPlayStatusIcon /> </MprisPlayPauseButton> <MprisNextButton flat> <Icon icon="media-skip-forward" /> </MprisNextButton> </Box> <MprisLengthLabel $type="end" /> </Gtk.CenterBox> </Box> </Box> )} </MprisList>
Theming
Most components take in Tailwind style props for border-radius
, margin
and padding
.
For more customized styles there is a builtin useStyle
hook, which can be used to defined scoped CSS for each component.
function MyComponent() {
const style = useStyle({
"background-color": "red",
"border": "2px solid blue",
"&": {
">child": {
"border-radius": "12px",
},
},
})
return <Box class={style} />
}
The default theme is derived from Adwaita, but you can customize it to your liking using the Theme
API or apply global CSS.
import { css, calc, variables as v } from "marble/theme"
// globally apply CSS making use of CSS variables used by Marble
void css`
tooltip {
background-color: ${v.bg};
margin: ${calc(v.spacing, 7)};
padding: ${calc(v.padding, 5)} ${calc(v.padding, 7)};
border-radius: ${calc(v.roundness, 9)};
border: none;
box-shadow:
${v.shadow.sm},
inset 0 0 0 ${v.borderWidth} ${v.borderColor};
}
`
import { Theme } from "marble/services"
export function initTheme() {
const theme = Theme.get_default()
const nucharm = new Theme.Stylesheet("Nucharm", {
stylesheet: {
dark: {
bg: "#151516", // overrides the above used `${v.bg}` value
fg: "#cfcfcf",
primary: "#51a4e7",
success: "#00d787",
error: "#e55f86",
},
light: {
bg: "#fafafa",
fg: "#090909",
primary: "#426ede",
success: "#009e49",
error: "#b13558",
},
},
})
theme.addTheme(nucharm)
// can be later activated, e.g through a widget
nucharm.activate()
}
Launcher Component
Marble exports two components PickerModal
and PickerEntry
to build a UI for Gnofi.
const gnofi = new Gnofi.Gnofi({ keys: Gdk, commandLeader: ":" })
// add a search provider picker
const filesProvider = new Gnofi.SearchPicker({
command: "f",
maxItems: 6,
icon: "org.gnome.Nautilus",
description: _("Search files using Nautilus"),
provider: {
busName: "org.gnome.Nautilus",
objectPath: "/org/gnome/Nautilus/SearchProvider",
desktopId: "org.gnome.Nautilus.desktop",
},
})
picker.addPicker(filesProvider)
return (
<PickerModal gnofi={gnofi}>
<Box vertical>
<PickerEntry gnofi={gnofi} />
{/* UI for each Gnofi picker */}
</Box>
</PickerModal>
)
TIP
You can use Gnofi.findProviders
to get a list of installed apps that can be used as providers. The default shell config also exposes it as a CLI command marble providers
.
The default configuration comes with these pickers.
The default search picker includes the search providers too.
OSD
<OSD vertical halign="end" valign="center" />
Lockscreen
You can create a custom lockscreen.
<Lockscreen open={lockscreenOpen} onUnlock={setLockscreenOpen}>
{({ loading, error, unlock }) => (
<>
<Box $type="overlay" halign="center" valign="start">
<ClockLabel mt={120} weight="bold" size={7.4} format="%H:%M" />
</Box>
<Box $type="overlay" halign="center" valign="center">
<Box>
<Gtk.PasswordEntry
$={(self) => onMount(() => self.grab_focus())}
showPeekIcon
onActivate={({ text }) => unlock(text)}
/>
<Adw.Spinner visible={loading} />
</Box>
<Box halign="center" valign="end">
<Text visible={error} color={v.error}>
{_("Authentication Failed")}
</Text>
</Box>
</Box>
</>
)}
</Lockscreen>