Skip to content

Instantly share code, notes, and snippets.

@mstr2
Last active May 29, 2025 17:44
Show Gist options
  • Save mstr2/cbb93bff03e073ec0c32aac317b22de7 to your computer and use it in GitHub Desktop.
Save mstr2/cbb93bff03e073ec0c32aac317b22de7 to your computer and use it in GitHub Desktop.

Media feature queries

In JavaFX 22, we added Platform Preferences to enable applications adjust their styling to OS preferences. However, except for replacing its stylesheets entirely, there is no easy way for an application to adjust its stylesheets at runtime.

We propose to add media queries to JavaFX, which is a powerful feature that allows stylesheets to dynamically test certain aspects of the JavaFX scene. Media queries are independent of the contents of the scene graph, its styling, or any other internal aspect; they’re only dependent on "external" configuration of the scene.

A media query is defined in a stylesheet as follows:

.button {
  -fx-background-color: lightgray;
}

@media (prefers-color-scheme: dark) {
  .button {
    -fx-background-color: darkgray;
  }
}

In this example, the button's background color will change at runtime when the preferred color scheme is changed to dark. Since a media query can contain arbitrary CSS rules, all aspects of styling can be adjusted.

Goals

Non-Goals

It is not a goal to

  • implement any other form of media feature query
  • support the range syntax for media queries, as it is not required for user-preference features
  • support media types
  • support the only keyword

Description

JavaFX CSS does not support the concept of media types; it only supports media features. A media query tests one or several media features. JavaFX supports the following user-preference media features:

Media feature Values
prefers-color-scheme light | dark
prefers-reduced-motion no-preference | reduce
prefers-reduced-transparency no-preference | reduce
prefers-reduced-data no-preference | reduce
-fx-prefers-persistent-scrollbars no-preference | persistent

These media features correspond to the colorScheme, reducedMotion, reducedTransparency, reducedData, and prefersPersistentScrollBars platform preferences.

Media query context

Media queries are not evaluated against the platform preferences directly, but instead against the Scene that contains the styleable scene graph. If we evaluted media queries against the platform preferences directly, application developers would have no way of providing in-app toggles, e.g. an option to change the application color scheme independently from the operating system.

Therefore, the following Scene.Preferences interface is added (along with the Scene.getPreferences() getter):

/**
 * Contains scene preferences that can override {@link Platform.Preferences platform} preferences.
 * <p>
 * All preferences are <em>null-coalesting</em> properties: if set to {@code null} (using the setter method,
 * {@link Property#setValue(Object)}, or with a binding), the property evalutes to the value of the corresponding
 * platform-provided preference. Likewise, specifying a non-null value for any given property will override the
 * platform-provided value.
 *
 * @see Platform.Preferences
 * @since 25
 */
public sealed interface Scene.Preferences {

    /**
     * Specifies whether the scene should prefer light text on dark backgrounds, or dark text
     * on light backgrounds.
     *
     * @return the {@code colorScheme} property
     * @defaultValue {@link Platform.Preferences#getColorScheme()}
     * @see Platform.Preferences#colorSchemeProperty()
     */
    ObjectProperty<ColorScheme> colorSchemeProperty();

    ColorScheme getColorScheme();

    void setColorScheme(ColorScheme colorScheme);

    /**
     * Specifies whether applications should always show scroll bars. If set to {@code false}, applications
     * may choose to hide scroll bars that are not actively used, or make them smaller or less noticeable.
     *
     * @return the {@code persistentScrollBars} property
     * @defaultValue {@link Platform.Preferences#isPersistentScrollBars()}
     * @see Platform.Preferences#persistentScrollBarsProperty()
     */
    ObjectProperty<Boolean> persistentScrollBarsProperty();

    /**
     * Gets the value of the {@code persistentScrollBars} property.
     *
     * @return the value of the {@code persistentScrollBars} property
     * @see #persistentScrollBarsProperty()
     * @see #setPersistentScrollBars(Boolean)
     */
    boolean isPersistentScrollBars();

    /**
     * Sets the value of the {@code persistentScrollBars} property.
     *
     * @param value the value
     * @see #persistentScrollBarsProperty()
     * @see #isPersistentScrollBars()
     */
    void setPersistentScrollBars(Boolean value);

    /**
     * Specifies whether the scene should minimize the amount of non-essential animations,
     * reducing discomfort for users who experience motion sickness or vertigo.
     *
     * @return the {@code reducedMotion} property
     * @defaultValue {@link Platform.Preferences#isReducedMotion()}
     * @see Platform.Preferences#reducedMotionProperty()
     */
    ObjectProperty<Boolean> reducedMotionProperty();

    /**
     * Gets the value of the {@code reducedMotion} property.
     *
     * @return the value of the {@code reducedMotion} property
     * @see #reducedMotionProperty()
     * @see #setReducedMotion(Boolean)
     */
    boolean isReducedMotion();

    /**
     * Sets the value of the {@code reducedMotion} property.
     *
     * @param value the value
     * @see #reducedMotionProperty()
     * @see #isReducedMotion()
     */
    void setReducedMotion(Boolean value);

    /**
     * Specifies whether the scene should minimize the amount of transparent or translucent
     * layer effects, which can help to increase contrast and readability for some users.
     *
     * @return the {@code reducedTransparency} property
     * @defaultValue {@link Platform.Preferences#isReducedTransparency()}
     * @see Platform.Preferences#reducedTransparencyProperty()
     */
    ObjectProperty<Boolean> reducedTransparencyProperty();

    /**
     * Gets the value of the {@code reducedTransparency} property.
     *
     * @return the value of the {@code reducedTransparency} property
     * @see #reducedTransparencyProperty()
     * @see #setReducedTransparency(Boolean)
     */
    boolean isReducedTransparency();

    /**
     * Sets the value of the {@code reducedTransparency} property.
     *
     * @param value the value
     * @see #reducedTransparencyProperty()
     * @see #isReducedTransparency()
     */
    void setReducedTransparency(Boolean value);

    /**
     * Specifies whether the scene should minimize the amount of internet traffic, which users
     * might request because they are on a metered network or a limited data plan.
     *
     * @return the {@code reducedData} property
     * @defaultValue {@link Platform.Preferences#isReducedData()}
     * @see Platform.Preferences#reducedDataProperty()
     */
    ObjectProperty<Boolean> reducedDataProperty();

    /**
     * Gets the value of the {@code reducedData} property.
     *
     * @return the value of the {@code reducedData} property
     * @see #reducedDataProperty()
     * @see #setReducedData(Boolean)
     */
    boolean isReducedData();

    /**
     * Sets the value of the {@code reducedData} property.
     *
     * @param value the value
     * @see #reducedDataProperty()
     * @see #isReducedData()
     */
    void setReducedData(Boolean value);
}

All properties are null-coalescing: if set to null, they evaluate to their respective platform value. This provides a way for users to specify "use the platform value".

Scene preferences allow developers to create multi-window applications where each window can be independently configured.

Future enhancements

In addition to user-preference media features, we can also add viewport media features like width, height, and orientation.

We can add media types (screen, print), but this requires a change in how JavaFX prints scene graphs. At the moment, JavaFX doesn't have a print-only CSS pass.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment