4

I am building a small password manager using JavaFX and currently working on make it easy to change the color theme. For that, I declared variables containing the hexcodes of different colors and saved'em in a stylesheet called "utility.css". This stylesheet is imported in two different stylesheets and the variables are called as described here.

Somehow I get this error while parsing:

Feb. 21, 2023 5:32:48 PM javafx.css.CssParser parse
WARNING: CSS Error parsing file:/G:/.../Coding/.../target/classes/css/utility.css: Expected RBRACE at [3,4]
Feb. 21, 2023 5:32:48 PM javafx.css.CssParser term
WARNING: CSS Error parsing file:/G:/.../Coding/.../target/classes/css/login-menu.css: Unexpected token '-' at [4,30]
Feb. 21, 2023 5:32:48 PM javafx.css.CssParser term
WARNING: CSS Error parsing file:/G:/.../Coding/.../target/classes/css/login-menu.css: Unexpected token '-' at [4,30]
Feb. 21, 2023 5:32:48 PM javafx.css.CssParser reportException
WARNING: Please report java.lang.NullPointerException at:
    javafx.graphics@19/javafx.css.CssParser.term(CssParser.java:4689)
    javafx.graphics@19/javafx.css.CssParser.expr(CssParser.java:4583)
    javafx.graphics@19/javafx.css.CssParser.declaration(CssParser.java:4554)
    javafx.graphics@19/javafx.css.CssParser.declarations(CssParser.java:4472)
    javafx.graphics@19/javafx.css.CssParser.parse(CssParser.java:3981)
    javafx.graphics@19/javafx.css.CssParser.parse(CssParser.java:253)
    javafx.graphics@19/javafx.css.CssParser.parse(CssParser.java:241)

I already asked chatGPT but seems there are things, AI can't help us with (yet) :D

This is the code of the "utility.css"-file, where I declared the variables

:root {
    --maincolor: #2b284d;
    --backgrounddark: #2b284d;
    --backgroundlight: #5a4f7d;
    --panel: #3c325c;
    --panelhighlighted: #503d6b;
    --panelselected: #503d6f;
    --casualbutton: #2b284d;
    --casualbuttonhighlighted: #5d5478;
    --crucialbuttonhightled: #eb5b0e;
    --textfield: #2b288d;
    --textfill: #b5b5b5;
}

and this of the two stylesheets:

@import "utility.css";

.combo-box .list-cell:filled:selected {
    -fx-background-color: var(--panelhighlighted);
    -fx-text-fill: #b5b5b5 ;
    -fx-font-family: "Calibri Light" ;
    -fx-font-size: 17;
    -fx-background-radius: 20px;
}

.combo-box .combo-box-popup .list-view, .combo-box .combo-box-popup .list-cell {
    -fx-background-color: var(--panel);
    -fx-text-fill: var(--textfill) ;
    -fx-font-family: "Calibri Light" ;
    -fx-font-size: 17;
    -fx-background-radius: 20px;
}

.combo-box .combo-box-popup .list-cell:hover {
    -fx-text-fill:  var(--textfill);
    -fx-background-color: var(--panelhighlighted);
}

.password-field {
    -fx-text-fill: #b5b5b5 ;
    -fx-prompt-text-fill: #b5b5b5 ;
}

.anchor-pane {
    overflow:hidden;
    -fx-background-color: #2b284d;
}

and

@import "utility.css";

.list-cell:filled:selected:focused, .list-cell:filled:selected {
    -fx-background-color: var(--panelselected);
}

.list-cell:filled:hover {
    -fx-background-color: var(--panelhighlighted);
}

.list-cell {
    -fx-background-color: var(--panel);
    -fx-pref-height: 60;
    -fx-background-radius: 20px;
    -fx-background-insets: 0;
    -fx-text-fill: var(--textfill) ;
    -fx-font-family: "Calibri Light" ;
    -fx-font-size: 20;
    -fx-alignment: center-left;
}

.list-view {
    -fx-background-color: var(--backgrounddark);
    -fx-border-color: var(--backgrounddark);
    -fx-background-insets: 0;
    -fx-padding: 0px;
}

.password-field {
    -fx-text-fill: var(--textfill) ;
    -fx-prompt-text-fill: var(--textfill) ;
    -fx-background-radius: 20px;
    -fx-background-color: var(--textfield);
}

.label {
    -fx-text-fill: var(--textfill);
    -fx-background-color: var(--textfield);
}

So I tried to eliminate '-' in the variable names, because these are used in css as the start of a command or sth but this didn't work out. I also tried an online css debugging tool, which only said I should switch the import statement with something called link to improve performance, which is not important for me atm.

I have absolutely no idea, what's the problem here. I am using IntelliJ 2022.3.1 (Ultimate Edition). The error happens at runtime, before that, IntelliJ doesn't complain at all and even auto-completes my variable names in the stylesheets, so they are definitely there and definitely correct.

The error appears to happen, when the css parser reaches the firtst '-' in the first stylesheet that calls a variable, as if this may not happen there.

Slaw
  • 37,820
  • 8
  • 53
  • 80
human0id
  • 43
  • 2
  • 1
    1. I think you mean `.root`, not `:root`. If you really intend to match a pseudoclass called `root`, you probably need `*:root`. 2. You don't need `var(...)`. Just do, for example, `-fx-background-color: --panelhighlighted;`. – James_D Feb 21 '23 at 17:10
  • 1
    For reasons I don't fully understand, the double hyphen on the looked-up color names is causing a parse error. If you rename them `-maincolor` etc, it works. – James_D Feb 21 '23 at 17:16
  • 1
    Actually, glancing quickly at the [CSS spec](https://www.w3.org/TR/CSS21/syndata.html#q4.0), I don't think `--maincolor` is a valid identifier. – James_D Feb 21 '23 at 17:20
  • Thanks for your quick response, sadly this didn't help. So I tried both, changing to .root and *:root but neither worked. They used :root at my source of knowledge, linked above. I also removed the var(--...) formatting which lead to an ignoring of the line of code (in the way that the following lines where read correctly (f.e.: -fx-backround-radius: 20px). This is also supported by the missing small quadrangle at the left next to the line of code in the IDE that did show the correct color when using the var(--...) format :/ Edit:This is a response to 1st answer, trying the 2nd and 3rd – human0id Feb 21 '23 at 17:24
  • Thanks James, I don't understand it at all but this worked for me. To be exact I removed the second hyphen everywhere. In the utility stylesheet where I defined the variables, I got the warning, that this would be an "Unknown CSS property" now and IntelliJ offered to add it to sth called "custom properties" which worked out fine. Think this is more of a workarround, if u understand whats happening here, feel free to enlight me. Thanks :) – human0id Feb 21 '23 at 17:30
  • 1
    See answer. The warnings you are getting from IntelliJ are probably assuming this is CSS designed for a web page, which it's not. I'm not aware of a JavaFX-CSS aware editor for IntelliJ. – James_D Feb 21 '23 at 17:35
  • 2
    You can learn a ton about this by looking at modena.css. – DaveB Feb 21 '23 at 18:08
  • 1
    JavaFX is based on CSS syntax, but not all HTML-specific extensions to CSS. In addition, JavaFX CSS supports features that are not available in HTML CSS. You cannot assume that some CSS will work for JavaFX, just because it CSS works in an HTML environment. The [JavaFX CSS reference](https://openjfx.io/javadoc/19/javafx.graphics/javafx/scene/doc-files/cssref.html) defines the capabilities of JavaFX CSS. If the capability isn't listed in the reference, then either JavaFX isn't capable of the feature or achieves it in a different way than HTML CSS. – jewelsea Feb 21 '23 at 19:23
  • 2
    Even for HTML CSS, the [var function specification](https://www.w3.org/TR/css-variables-1/) is only currently a "w3c candidate recommendation snapshot". While [looked-up colors](https://openjfx.io/javadoc/19/javafx.graphics/javafx/scene/doc-files/cssref.html#typecolor) achieve some of the features which you can get from a CSS `var` function, they are not really the same. Though, for the purposes of your question, you can readily substitute as demonstrated in [James' answer](https://stackoverflow.com/a/75523950/1155209). – jewelsea Feb 21 '23 at 19:28
  • @jewelsea Excellent points, as usual. I incorporated a little of that into an edit in the answer. – James_D Feb 21 '23 at 21:03
  • Thank y'all, definitely gonna read more about that :) – human0id Feb 21 '23 at 22:29

1 Answers1

4

Generally, CSS variables are not supported in JavaFX CSS. However, JavaFX CSS does support a (non-standard) mechanism called "looked-up colors" which effectively provides the ability to define color variables. This is documented here. The default modena.css stylesheet uses many looked-up colors, many of which are dependent on each other, to allow you to "theme" an application fairly readily by changing just a small number of the predefined looked-up colors. The current implementation of modena is here. See this question for an example of using this approach to "Theme" an application.

The rest of this answer shows you why your current solution is not working and how to use looked-up colors to achieve the same goal.

Glancing quickly at the CSS Specification, it looks like a valid identifier can only have a single hyphen at the start:

Macro     Definition
ident     [-]?{nmstart}{nmchar}*
name      {nmchar}+
nmstart   [_a-z]|{nonascii}|{escape}

The : in a selector signifies a pseudoclass. In HTML, the document root is assigned a pseudoclass called root. In JavaFX, the root of a scene is assigned a CSS class called root. So if you want the looked-up colors to be applied to the root pane and inherited by everything in the application, use the class selector for the root (.root). Note that :root is syntactically valid as a selector, but since you likely don't have anything with the root pseudoclass, it will generate errors at runtime because the looked-up colors won't be found (there is no node for which those looked-up colors are defined).

I did a quick test with all the looked-up color names renamed to a start with a single hyphen, and changed the selector to select the root:

.root {
    -maincolor: #2b284d;
    -backgrounddark: #2b284d;
    -backgroundlight: #5a4f7d;
    -panel: #3c325c;
    -panelhighlighted: #503d6b;
    -panelselected: #503d6f;
    -casualbutton: #2b284d;
    -casualbuttonhighlighted: #5d5478;
    -crucialbuttonhightled: #eb5b0e;
    -textfield: #2b288d;
    -textfill: #b5b5b5;
}

Note that CSS naming conventions recommend separating words with hyphens, so your looked-up color names should probably be something like -main-color, etc. You might also consider putting a vendor prefix on them to avoid name collisions.

There is no need for var(...) in JavaFX CSS to reference a looked-up color (and I believe the parser will not recognize it). Just reference these directly. See the JavaFX CSS documentation.

I tested this with a CSS stylesheet style.css as follows:

@import "utility.css";
.label {
    -fx-background-color: -backgrounddark;
}

and ran it with IntelliJ's "default" JavaFX template (which I included below), and it worked as expected with no errors.

HelloApplication.java:

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;

import java.io.IOException;

public class HelloApplication extends Application {
    @Override
    public void start(Stage stage) throws IOException {
        FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
        Scene scene = new Scene(fxmlLoader.load(), 320, 240);
        scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
        stage.setTitle("Hello!");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }
}

HelloController.java:

import javafx.fxml.FXML;
import javafx.scene.control.Label;

public class HelloController {
    @FXML
    private Label welcomeText;

    @FXML
    protected void onHelloButtonClick() {
        welcomeText.setText("Welcome to JavaFX Application!");
    }
}

and hello-view.fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>

<?import javafx.scene.control.Button?>
<VBox alignment="CENTER" spacing="20.0" xmlns:fx="http://javafx.com/fxml"
      fx:controller="org.jamesd.examples.csstest.HelloController">
    <padding>
        <Insets bottom="20.0" left="20.0" right="20.0" top="20.0"/>
    </padding>

    <Label fx:id="welcomeText"/>
    <Button text="Hello!" onAction="#onHelloButtonClick"/>
</VBox>
James_D
  • 201,275
  • 16
  • 291
  • 322