Skip to content
CrossFly is 100% open sourceFollow us on GitHub
CrossFlyCrossFly
Get started

This page was machine-translated from Portuguese and may contain errors. The Portuguese version is canonical.

Light/dark mode

CrossFly switches between light and dark in real time and in place — without recreating screens. The key is Styles.setDarkMode(...) combined with styles that re-resolve from the theme.

Complete example (copy, paste and run)

package com.exemplo;

import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;
import dev.crossfly.ui.style.*;   // Styles, ThemeMode, LabelRole

/** Runnable main screen. Point the launcher (classname) and run: gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      Styles.setColorScheme(0xFF6750A4, ThemeMode.LIGHT);   // base palette (follows light/dark)
      setLayout(new VerticalLayout());

      // styled(...) re-evaluates the style on each theme switch (automatic re-skin per mode)
      Label titulo = new Label("Bem-vindo")
         .styled(() -> Styles.theme().label(LabelRole.TITLE));
      add(LAY_AUTO, titulo);

      // Switch toggles the mode; setDarkMode re-skins the whole open UI on its own.
      Switch alternar = new Switch("Modo escuro");
      alternar.onAction(c -> Styles.setDarkMode(alternar.isChecked()));
      add(LAY_AUTO, alternar);
   }
}

The snippets below show only the core (what goes inside onShow()). To run them, place them in the MainWindow skeleton above.

  1. Use theme PalColor (Styles.textColor, cs.surface) in styles — they re-resolve themselves.
  2. Call setDarkMode(...):
Styles.toggleDarkMode();              // toggles light ↔ dark
Styles.setDarkMode(true);             // forces dark
boolean escuro = Styles.isDarkMode(); // current state

setDarkMode(...) already re-applies the theme to all open windows automatically (it triggers restyleAll() internally), so styles bound to PalColor change color without any extra code.

Styles that depend on the mode → styled(...) / onRestyle(...)

When the style changes shape according to the mode (not just the color), register how the component should re-style itself. The callback runs once immediately and again on each theme switch:

Label titulo = new Label("Bem-vindo")
   .styled(() -> Styles.theme().label(LabelRole.TITLE));   // re-evaluated on each theme switch

// for logic beyond style (swap icon glyph, chart color, etc.):
Label l = new Label("Status");
l.onRestyle(() -> l.style(Styles.isDarkMode() ? estiloEscuro : estiloClaro));

Manual re-skin of a subtree

If you changed the theme non-globally (e.g., just one panel), force the reapplication:

window.restyle();   // re-runs all onRestyle in the tree and repaints, without recreating components

Complete flow

Switch alternar = new Switch("Modo escuro");
alternar.onAction(c -> Styles.setDarkMode(alternar.isChecked()));  // the whole UI follows along

Best practices

  • Prefer theme PalColor over fixed colors — dark mode becomes automatic.
  • For styles that change structure per mode, use styled(...) (leaner than onRestyle).
  • Read the mode with Styles.isDarkMode() / Styles.themeMode() when building styles.

Common mistakes

  • Storing fixed colors (0xFF...) and then having to rebuild the screen by hand for dark mode.
  • Rebuilding the entire UI when switching theme — use restyle()/styled(...) and reuse the components.

See also

Was this page helpful?