Skip to content

Features

By default Marble comes with a prebuild desktop shell.

desktop

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

    tsx
    const [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>

    notification

  • 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>

    media player

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.

ts
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.

ts
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.

tsx
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.

launcher-help

The default search picker includes the search providers too.

launcher

OSD

tsx
<OSD vertical halign="end" valign="center" />

osd

Lockscreen

You can create a custom lockscreen.

tsx
<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>