CSS

Mavenize your Sass

Sass is a kind of precompiler for CSS that offers additional features like nested styles, variables or includes. The syntax of scss files is similar to standard CSS, so it’s very easy to learn.

My goal is to:

  • nicely integrate Sass into the Maven build process,
  • use Jetty to see the web application with the compiled css files, and
  • see changes to a Sass file live (without running Maven or restarting Jetty).

There is a sass-maven-plugin by the Apereo Foundation that already offers Maven integration and watching changes to sass files. However, I don’t fancy the default settings. The default location of sass files is the webapp directory, which means that the sass files are packed into the war file as well, exposing silent comments, variable names and maybe other things that are not meant to be published.

I prefer src/main/sass as source directory. As target, I choose a directory called generated-webapp, which is ought to contain all generated webapp stuff. In this directory, a css directory will contain the compiled css files. The sass-maven-plugin configuration now looks like this:

<plugin>
    <groupId>org.jasig.maven</groupId>
    <artifactId>sass-maven-plugin</artifactId>
    <version>1.1.1</version>
    <executions>
        <execution>
            <id>package</id>
            <phase>prepare-package</phase>
            <goals>
                <goal>update-stylesheets</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <resources>
            <resource>
                <source>
                    <directory>${basedir}/src/main/sass/</directory>
                </source>
                <destination>${project.build.directory}/generated-webapp/css</destination>
            </resource>
        </resources>
    </configuration>
</plugin>

The reason for using generated-webapp instead of ${project.build.directory}/${project.build.finalName} is that Jetty only looks into src/main/webapp for static resources by default. However, if ${project.build.directory}/${project.build.finalName} was added, Jetty would also scan all compiled classes twice, which may result in errors (especially when Spring is involved). What needs to be done now is to add generated-webapp to the list of webapp resources:

<plugin>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-maven-plugin</artifactId>
    <version>9.2.2.v20140723</version>
    <configuration>
        <webApp>
            <resourceBases>
                <directory>${basedir}/src/main/webapp</directory>
                <directory>${project.build.directory}/generated-webapp</directory>
            </resourceBases>
        </webApp>
    </configuration>
</plugin>

After starting Jetty with mvn jetty:run, it nicely returns our css resources.

To make changes to Sass files immediately available, the Sass watcher is started in a second terminal with mvn sass:watch. Jetty will deliver all changes in src/main/sass now, without the need to restart.

A final problem to solve is that the generated-webapp target is not included in the war file by default. The maven-war-plugin needs to be configured properly:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.4</version>
    <configuration>
        <webResources>
            <resource>
                <directory>${project.build.directory}/generated-webapp</directory>
            </resource>
        </webResources>
    </configuration>
</plugin>
JSF2.0, mod_proxy and CSS

I stumbled upon a strange issue when I recently deployed a JSF 2.0 web application. The application was developed using Jetty, and was working fine there. However when I deployed the application on a server, the page layout was utterly broken and Firefox complained that it would not load some CSS resources because of the wrong content type application/xml+xhtml.

On the server, a Tomcat is connected to an Apache web server via mod_proxy and mod_proxy_ajp. I found out that the Tomcat itself delivered the CSS resources without a Content-Type header, but the Apache web server returned a Content-Type: application/xml+xhtml. Actually, mod_proxy always adds a Content-Type header if it is missing, and tries to guess the right content type by the file suffix.

I searched the net for a proper solution of this issue, but found none. After some experiments, a working solution was to configure the Apache web server to force the content type of all .css.xhtml files to text/css:

<LocationMatch "\.css\.xhtml$">
    ForceType text/css
</LocationMatch>

However I don’t know if this is the best practice. A likely better solution would be the Mojarra Faces servlet to return a proper content type for the CSS files.