Styling: CSS & GSS

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

This page is written against GWT 2.13.1 (released 2026-06-19), gwt-maven-plugin 1.3.0 (net.ltgt.gwt.maven), JDK 21 running the compiler with -sourceLevel 17, the Jakarta Servlet 6 server of the Bookshelf scenario and the gwt-user 2.13.1 sources, checked on 2026-10-09. It is mostly client knowledge: the style sheets are processed by the GWT compiler at build time (a deferred-binding generator turns every CssResource into a Java class) and injected into the page in the browser. There is no server code. Every output below was produced by compiling probe modules with the 2.13.1 compiler (-strict) and loading them in headless Chrome 155, and the Super Dev Mode statement at the end of the worked example was checked against a running code server on port 9911 (the probe pages were served from port 8911). Where the page quotes a compiler message it is copied from those runs, and a few statements come from the Developer Guide or the 2.13.1 source only; those say so.

How a GWT page gets its styles

A GWT application is an ordinary web page, so everything the browser knows about CSS applies; GWT adds four ways to attach style sheets, a set of ready-made themes and, most importantly, a compiler pass for CSS. Plain CSS selectors, specificity and the cascade are not repeated here: the HTML & CSS selectors and specificity page covers them.

flowchart LR subgraph s1["Sources you write"] th["Theme stylesheet"] hp["link in host page or module stylesheet"] cr["CssResource files, css or gss"] ub["ui:style blocks in UiBinder"] end subgraph s2["GWT compiler at build time"] gen["CssResourceGenerator, enableGss decides"] conv["Css2Gss conversion, conversionMode"] obf["obfuscate, evaluate if blocks, inline images"] end subgraph s3["Browser at run time"] inj["StyleInjector style element"] dom["DOM class attributes"] end th -->|"link element"| dom hp -->|"link element"| dom cr --> gen ub --> gen gen -->|"css file and conversion not off"| conv gen --> obf conv --> obf obf -->|"ensureInjected"| inj inj --> dom

The left column is the choice you make; the middle column is what the compiler does per permutation; the right column is what the browser receives.

Table 1. The four ways to attach CSS (Developer Guide, "Associating CSS Files")
Way How Status in the Developer Guide

Host page link

<link rel="stylesheet" href="mystyles.css"> in the host HTML

Marked "Deprecated". Works, but the file is not part of the module and is not processed by GWT

Module stylesheet

<stylesheet src="gwt/clean/clean.css"/> in the *.gwt.xml file (see Project Structure & Modules)

Marked "Deprecated". The file is found through the module’s public path and "follows the module" to any host page

CssResource in a ClientBundle

An interface that extends CssResource, returned by a bundle method; the compiler reads the file

The modern way; this page’s main subject

ui:style in a UiBinder template

CSS written next to the markup that uses it, or loaded with src

The modern way, combined with CssResource

The 2007 book on which part of this guide leans taught only the first two rows (a Dashboard.css file next to the module and <stylesheet>, plus setStyleName), because ClientBundle, CssResource, UiBinder and GSS did not exist yet. That advice still works; this page shows what replaced it and why.

Style names on widgets

Every widget is a DOM element with a class attribute, and the widget classes manage that attribute with a small family of methods. The Widgets page lists the methods and the probe that shows setStylePrimaryName renaming the dependent names too; this section adds the naming convention the themes rely on.

Each widget class has a primary style name of the form gwt-<ClassName> (gwt-Button, gwt-TextBox, gwt-Label), set by its constructor. A dependent style name is the primary name plus a suffix and describes a state (gwt-Button with gwt-Button-busy after addStyleDependentName("busy")). A secondary name is any other class you add with addStyleName. Composite widgets put several names on several elements. A probe in the Clean theme printed the class attributes of four composite widgets:

Table 2. Class attributes observed on widgets (Clean theme, 2.13.1)
Widget Rendered class names

MenuBar with the first item selected

div.gwt-MenuBar.gwt-MenuBar-horizontal, then td.gwt-MenuItem.gwt-MenuItem-selected for the selected item and td.gwt-MenuItem for the others

TabBar with the first tab selected

table.gwt-TabBar, div.gwt-TabBarFirst, td.gwt-TabBarItem-wrapper.gwt-TabBarItem-wrapper-selected, div.gwt-TabBarItem.gwt-TabBarItem-selected, div.gwt-TabBarRest

Button after addStyleName("primary") and addStyleDependentName("busy")

gwt-Button primary gwt-Button-busy

read-only TextBox

gwt-TextBox gwt-TextBox-readonly

DialogBox with a caption

div.gwt-DialogBox, caption element div.Caption

The Developer Guide’s MenuBar example (a gwt-MenuItem-selected suffix and the pair of names “gwt-MenuItem gwt-MenuItem-selected”) matches the observed output. Which names a widget uses is documented in its Javadoc, not in one table: the guide says it is "standard practice" to document the relevant style names in each widget class’s comment, for example in UIObject and the widget classes. When a widget has no documented hook, read the DOM produced by getElement().getString(), as Widgets does.

Two rules keep a theme and your own CSS working together:

  • Add your names with addStyleName and use setStyleName(String) only when you mean to leave the theme (it replaces the whole attribute, so the gwt- primary name is gone and so are all theme rules)

  • give your own widgets a primary name with a prefix of your project (bookshelf-BookCard) and dependent names for state (bookshelf-BookCard-selected), the same convention the SDK uses, so that RTL and theme overrides can target them

The Developer Guide also covers addressing one element by id: GWT sets no id for you, so set it yourself on a static element in the host page or on a widget’s DOM element, and reference it as #my-button-id. IDs must be unique in the page, which is why classes are the normal hook; ensureDebugId (see Widgets) is for tests.

Default themes

GWT ships four visual themes, and a new application inherits one of them in its module XML. The Developer Guide names three (Standard, Chrome and Dark, "by default, new GWT applications use the standard theme") and says each one also comes in an RTL version and a "Resources" version; the 2.13.1 source also contains a fourth, Clean, which the generated Bookshelf module uses.

Table 3. The four themes of gwt-user 2.13.1
Theme Module to inherit Stylesheet it adds Size on disk RTL module

Clean

com.google.gwt.user.theme.clean.Clean

gwt/clean/clean.css

24,581 bytes

com.google.gwt.user.theme.clean.CleanRTL (clean_rtl.css)

Standard

com.google.gwt.user.theme.standard.Standard

gwt/standard/standard.css

22,259 bytes

StandardRTL (standard_rtl.css)

Chrome

com.google.gwt.user.theme.chrome.Chrome

gwt/chrome/chrome.css

22,430 bytes

ChromeRTL (chrome_rtl.css)

Dark

com.google.gwt.user.theme.dark.Dark

gwt/dark/dark.css

20,481 bytes

DarkRTL (dark_rtl.css)

Each theme module is one line of substance: <stylesheet src="gwt/clean/clean.css"/> plus the public folder with the theme’s images (Clean has six small images). The User module itself inherits no theme.

<module rename-to="bookshelf">
  <inherits name="com.google.gwt.user.User"/>
  <!-- choose at most one theme; the Bookshelf uses Clean -->
  <inherits name="com.google.gwt.user.theme.clean.Clean"/>
  <!-- <inherits name="com.google.gwt.user.theme.standard.Standard"/> -->
  <!-- <inherits name="com.google.gwt.user.theme.chrome.Chrome"/> -->
  <!-- <inherits name="com.google.gwt.user.theme.dark.Dark"/> -->
</module>

To see what a theme does I compiled six modules that differ only in the inherits line and printed what the browser applied to one Button, one TextBox and the page body:

Table 4. What each module produced (headless Chrome 155)
Module Injected link Computed style

Clean

themeclean/gwt/clean/clean.css

Button font "Arial Unicode MS", Arial, sans-serif at 13px, border 1px; text box border 1px solid; body white

Standard

themestandard/gwt/standard/standard.css

Button font Arial 13px, border 1px; text box border 2px inset

Chrome

themechrome/gwt/chrome/chrome.css

as Standard (same Button border colour rgb(204, 204, 204))

Dark

themedark/gwt/dark/dark.css

body rgb(77, 77, 77) with text rgb(190, 199, 204), Button border black

DarkRTL

themedarkrtl/gwt/dark/dark_rtl.css

as Dark

no theme

none: no link element in the page

Button with the browser default (background rgb(239, 239, 239), 2px border, 13.3333px font), body transparent with the browser’s serif font

Dropping or replacing a theme

To drop the themes, remove the inherits line; the page then only has the browser defaults plus whatever you add. This is the normal choice for an application with its own design system, and for a design built by a team that wants to control every class. Because the theme is a plain link to a file in the module, removing it does not change any Java code; widgets keep their gwt- class names, so your CSS can still target them.

Each theme also has a Resources variant (CleanResources, StandardResources, ChromeResources, DarkResources) described by the guide as "only the public resources associated with the theme, but does not inject a style sheet". The probe confirmed it: a module that inherits StandardResources published gwt/standard/standard.css, gwt/standard/standard_rtl.css and the images folder into the output, and no link element was added to the page. You copy that file into your own style sheet, remove what you do not need and attach the result with one of the four ways above; the guide recommends it for bandwidth-sensitive applications, where it puts the cost of a whole theme at "about 27k". The measured sizes are 20 to 25 kilobytes uncompressed. In the 2.13.1 source the Standard, Chrome and Dark Resources modules are empty <module/> files (the files are copied because they live in the theme’s public folder), while CleanResources inherits User and declares <public path="public"/>, so the effect is the same.

A third option is to keep a theme and override only some rules; the worked example does this by declaring the gwt- class names @external in a GSS file, so that the compiler leaves them unobfuscated.

CssResource

CssResource is the interface that represents one compiled style sheet. You declare a subtype with one String accessor per CSS class, return it from a ClientBundle method and call ensureInjected() before the styles are needed. The compiler reads the file, checks it against the interface, renames the classes and writes a Java class that returns the final text. The ClientBundle & Images page covers the other resource types and the bundle machinery (@Source, inlining, caching); this page covers the CSS-specific part.

public interface BookshelfCss extends StateCss {   // StateCss extends CssResource
  String header();

  @ClassName("low-stock")                           // CSS class is not a Java identifier
  String lowStock();

  String cover();

  String ribbon();

  int COVER_WIDTH();                                 // a numeric @def
}

public interface BookshelfResources extends ClientBundle {
  BookshelfResources INSTANCE = GWT.create(BookshelfResources.class);

  @Source("bookshelf.gss")
  BookshelfCss css();
}

Facts worth knowing before the details:

  • There is one resource type, CssResource. The Developer Guide talks about "GssResource", but there is no such class in gwt-user 2.13.1: the interface is CssResource for both syntaxes (its annotation @DefaultExtensions({".css", ".gss"}) lists both extensions) and the class CssResourceGenerator delegates to GssResourceGenerator when CssResource.enableGss is true. The page treats "GssResource" as that generator.

  • ensureInjected() calls StyleInjector, which collects style sheets and flushes them with Scheduler.scheduleFinally, so the style element appears at the end of the current event loop, not in the call. In the probe, getComputedStyle read synchronously in onModuleLoad still returned the theme colour; after a deferred command it returned the override. Repeated calls are no-ops and return true only when they changed the DOM.

  • getText() returns the final style sheet as a string, which is how the outputs on this page were captured.

  • Without @Source the compiler looks for a file named after the accessor next to the interface: css() finds css.css or css.gss (with enableGss true the .gss file is tried first).

  • The generated class is created per permutation, so @if blocks on user.agent or locale become different text in different permutations (see the directives table).

Obfuscated class names

The compiler renames every class you reach through an accessor, so two style sheets can both define .title without clashing and unused rules can be dropped. Strict checking is on by default: every class selector must have an accessor, and every accessor must have a class. The names are meant to be used through the accessor, never typed:

copies.addStyleName(RES.css().lowStock());   // "EFLPHAC-a-d", not "low-stock"

The text of an obfuscated class depends on two configuration properties of the Resources module (both read from Resources.gwt.xml in 2.13.1):

Table 5. Configuration properties that shape the output
Property Default Meaning

CssResource.style

obf

The obfuscation style. Source values: pretty, debug, stable, stable-shorttype, stable-notype; any other value (including the default) means fully obfuscated

CssResource.obfuscationPrefix

default

A prefix for every generated class. default derives one from the CssResource types; empty removes it (shortest names, "only recommended when the GWT module has total control over the page"); any other text is used as given

CssResource.reservedClassPrefixes

gwt-

Multi-valued: class-name prefixes the obfuscator must never generate

CssResource.mergeEnabled

true

Set to false to turn off the rule-merging optimisations (a temporary measure according to the guide)

CssResource.allowedAtRules

-moz-document, supports

Extra non-standard at-rules the parser accepts (multi-valued)

CssResource.allowedFunctions

empty

Extra non-standard functions the parser accepts (multi-valued)

For the same GSS file, these were the names of the card accessor of the probe’s BookCss (all five compiled from one file):

Table 6. The card class under each CssResource.style value (GSS, 2.13.1)
Setting Class name

default (obf)

GO1C0CD-b-c

obfuscationPrefix = empty

b-c

pretty

GO1C0CD-b-c-probe-gss-GRes-BookCss-card

debug

GO1C0CD-b-c-GRes-BookCss-card

stable

probe-gss-GRes-BookCss-card

stable-shorttype

GRes-BookCss-card

stable-notype

card (the widget of two different types then collided: both returned widget)

The stable* styles produce names that do not depend on the rest of the application, which makes them usable in tests and in tools that cache CSS. The source explains why the compiler uses one of them: Super Dev Mode with incremental compilation (the default) overrides CssResource.style to stable (in Recompiler), and the worked example confirmed it: in Super Dev Mode the class names were com-example-bookshelf-client-style-BookshelfCss-low-stock instead of an obfuscated token. Production builds use the default.

The -style PRETTY compiler flag formats the JavaScript only: a legacy module without CssResource.style printed minified, obfuscated CSS even when compiled with -style PRETTY. With CssResource.style = pretty, legacy CSS is also pretty-printed with line breaks, while GSS keeps its minified single line and only the class names become readable.

Scopes, ImportedWithPrefix, Shared

"Scope" in the guide is the set of obfuscated names that belong to one CssResource return type. Two accessors returning the same interface share names; two different interfaces (even when they both declare title()) get different names.

// A state class that must have the same name in every stylesheet that extends it
@Shared
public interface StateCss extends CssResource {
  String selected();
}

public interface BookshelfCss extends StateCss { String header(); /* ... */ }
public interface NavCss extends StateCss { String item(); }

public interface BookshelfResources extends ClientBundle {
  @Source("bookshelf.gss") BookshelfCss css();
  @Source("nav.gss") NavCss nav();
}

@Shared on the supertype makes css().selected() and nav().selected() return the same text; the run printed css.selected=EFLPHAC-a-f nav.selected=EFLPHAC-a-f shared=true. Each file that extends the shared type still has to contain a rule for it: the GSS compiler rejects an accessor without a class (The following method [focused()] doesn't match a constant nor a style class. You could fix that by adding .focused {}), so nav.gss in the example has .selected .item { ... }.

@Import and @ImportedWithPrefix solve the opposite problem: you need to write a descendant selector across two unrelated types whose accessors have the same name. The imported type gets a prefix (the annotation value, or the simple name of the type) and its classes are written .<prefix>-<name> in the importing file:

@ImportedWithPrefix("tree")
public interface TreeCss extends CssResource { String widget(); }

@ImportedWithPrefix("cb")
public interface CbCss extends CssResource { String widget(); }

public interface MyCss extends CssResource { String other(); }

public interface Resources extends ClientBundle {
  @Import({TreeCss.class, CbCss.class})
  @Source("my.gss")
  MyCss my();
}
.tree-widget .cb-widget { color: red; }
.other { margin: 0; }

The run compiled this to .GO1C0CD-e-a-probe-gss-TreeCss-widget .GO1C0CD-a-a-probe-gss-CbCss-widget{color:red} (pretty style), keeping each class in its own scope. It is an error to import two types with the same prefix. ui:style has an import attribute that does the same for a template (it takes a space-separated list of type names, and it exists in the generator source, UiBinderParser, although the guide does not describe it).

Two corrections to the guide’s text emerged:

  • @Shared names are shared inside one ClientBundle. The CssResource that UiBinder generates for a ui:style lives in its own generated bundle, and in the probe a ui:style whose type extended a @Shared interface did not get the same name as the same accessor of a hand-written bundle (global=BKJK6UC-a-a template=BKJK6UC-b-b equal=false in GSS; GAWRW50BKI against GAWRW50BJI in legacy CSS). Put shared state classes in one hand-written bundle and apply them from Java.

  • Annotation values that are class literals (@Import({TreeCss.class})) are read from compiled classes, so the compile classpath must contain target/classes and not only the source folders; with only sources the compiler failed with Annotation error: expected class java.lang.Class, got null and then a NullPointerException in InlineClientBundleGenerator. Maven and Gradle put the classes first, so this appears with hand-made command lines.

Names that are not Java identifiers, strictness and external classes

Three annotations and one at-rule decide what happens with class names that do not map one-to-one to accessors:

Tool Effect

@ClassName("low-stock") on the accessor

Maps a class that is not a Java identifier (.low-stock) to an accessor lowStock(). Without it the accessor binds to .lowStock

@NotStrict on the bundle method

Allows class selectors without accessors ("not recommended for new code"); useful while moving old CSS in

@external (CSS) and the same in GSS

Leaves named classes unobfuscated. The accessor of an external class returns the original name; classes you name there do not need an accessor

@Strict

A deprecated no-op: strict mode is the default

In legacy CSS @external takes selectors and wildcards: @external legacyBanner;, @external gwt-*;. In GSS the names have no leading dot and a wildcard must be quoted: @external legacyBanner; and @external 'gwt-*'; (a dot gave Parse error ... Encountered " "." ". ""). Both forms were compiled, and in both the unobfuscated name appeared verbatim: the probe printed legacyBanner=legacyBanner.

// the accessor of an @external class returns the original name
String legacyBanner();                  // "legacyBanner" in the output

The legacy CSS directives

Legacy CssResource syntax (the default in 2.13.1) is a CSS file with GWT at-rules. The Developer Guide lists the features; here they all appear in one Bookshelf file that I compiled with CssResource.style set to pretty and also left obfuscated.

@def coverWidth 96px;
@def accent #1a5fb4;
@eval lowStockColor probe.client.Palette.lowStockColor();
@url spineUrl spineData;
@external legacyBanner;

.card {
  width: coverWidth;
  border: 1px solid accent;
  margin-left: 8px;
  float: left;
}

.title { font-weight: bold; }
.low-stock { color: lowStockColor; }
.legacyBanner .title { text-decoration: underline; }
.spine { background-image: spineUrl; }

@sprite .star {
  gwt-image: "starIcon";
  display: inline-block;
}

@noflip {
  .pin { left: 4px; }
}

@if (probe.client.Palette.compact()) {
  .card { padding: 2px; }
}

@if user.agent safari {
  .title { -webkit-font-smoothing: antialiased; }
} @else {
  .title { font-smooth: always; }
}

The bundle supplies starIcon (an ImageResource) and spineData (a DataResource), and the interface has accessors card(), title(), @ClassName("low-stock") lowStock(), star(), spine(), pin(), legacyBanner() and int coverWidth(). The output of getText() (pretty style, data URLs shortened) was:

/* CssDef */
/* CssDef */
/* CssEval */
/* CssUrl */
/* @external legacyBanner; */
.GCWXV1BCHI-probe-client-Res-BookCss-card {
  width : 96px;
  border : 1px solid #1a5fb4;
  margin-left : 8px;
  float : left;
}
.GCWXV1BCOI-probe-client-Res-BookCss-title {
  font-weight : bold;
  -webkit-font-smoothing : antialiased;
}
.GCWXV1BCKI-probe-client-Res-BookCss-low-stock {
  color : #c01c28;
}
.legacyBanner .GCWXV1BCOI-probe-client-Res-BookCss-title {
  text-decoration : underline;
}
.GCWXV1BCMI-probe-client-Res-BookCss-spine {
  background-image : url('data:image/png;base64,...');
}
.GCWXV1BCNI-probe-client-Res-BookCss-star {
  height : 16px;
  width : 16px;
  overflow : hidden;
  background : url("data:image/png;base64,...") -0px -0px  no-repeat;
  display : inline-block;
}
/*@noflip { */
.GCWXV1BCLI-probe-client-Res-BookCss-pin {
  left : 4px;
}
/*} @noflip */
/* @if (probe.client.Palette.compact()) { */
.GCWXV1BCHI-probe-client-Res-BookCss-card {
  padding : 2px;
}
/* } */

Reading the output tells how each directive behaves:

Table 7. Legacy directives, as observed
Directive Behaviour

@def name value;

A constant, replaced where used. A numeric single-value @def is also readable from Java through an int, long, float or double accessor (int coverWidth() returned 96); any @def can be read as a String. The generator refuses a @def whose name is also a class unless the class accessor has @ClassName

@eval name expression;

A static Java method evaluated when the stylesheet is injected, or at compile time when the compiler can evaluate it. Palette.lowStockColor() became #c01c28 in the text

value('a.b', 'px')

Calls a chain of zero-argument methods on the bundle, such as the width of an ImageResource, with an optional unit

literal("...")

Passes a non-standard value through unchanged; backslash and double quote must be escaped

@url name resourceMethod;

Replaces the name with url(...) of a DataResource method of the same bundle; small resources become data: URLs (the probe’s PNG did)

@sprite .selector { gwt-image: "method"; ... }

Expands to the size, overflow and background of an ImageResource of the same bundle. The sprite and the repeatStyle of its @ImageOptions are the subject of ClientBundle & Images

@if with a Java call

Evaluated at run time in a static context, unless the compiler can fold it. The probe’s Palette.compact() returned true, so the block was kept (as a comment-wrapped rule)

@if user.agent safari { ... } @else { ... }

Evaluated at compile time per permutation, so the safari permutation contained one branch and the other permutation the other. Only gecko1_8 and safari exist as user.agent values in 2.13.1; the guide’s examples with ie6, ie8 and opera no longer apply. @elif and @else, negation (!) and nesting are supported

@external a, b;

Selectors not to obfuscate (see above)

@noflip { ... }

Protects its rules from RTL flipping (see "Right-to-left")

The guide’s remarks about merging and minification describe the optimiser: comments and empty rules are removed, identical selectors and identical property sets can be merged unless that would reorder properties that conflict. In the observed output the second .card rule (the @if block with padding) stayed separate, which is what the ordering rule predicts for a rule that could not be moved past the first.

For old CSS only, com.google.gwt.resources.css.InterfaceGenerator writes a CssResource interface from a style sheet: java -cp gwt-user.jar:gwt-dev.jar com.google.gwt.resources.css.InterfaceGenerator -addPackageHeader -typeName com.example.bookshelf.client.style.NavCss -css nav.gss. The flag in the guide, -standalone, still works as an alias; the usage text of 2.13.1 lists -[no]addPackageHeader. Run on a GSS file it produced String item(); and String selected(); (the interface it prints is package-private, with @ClassName("gwt-Button") String gwtButton(); for external classes, and it renders a numeric @def as a String accessor), so review its output before using it.

GSS

GSS is the second syntax understood by CssResource, and it is built on Google’s open-source Closure Stylesheets. It extends CSS3 syntax with constants (@def), mixins (@defmixin, @mixin), functions (mult and divide were compiled here, concat is a GWT addition), conditionals and loops. The migration article says that all of Google’s own applications have used GSS instead of CSS since mid 2015. GWT 2.7 introduced it as experimental; GWT 2.8.0 declared GSS no longer experimental and deprecated the old CssResource syntax, while keeping CSS as the default "for easier and smoother migration". In 2.13.1 the default is still CSS: GSS is opt-in with a configuration property, and the Developer Guide (updated for GSS) and the migration article encourage the move.

<!-- in the application's *.gwt.xml -->
<set-configuration-property name="CssResource.enableGss" value="true"/>
<set-configuration-property name="CssResource.gssDefaultInUiBinder" value="true"/>
<!-- conversionMode is off by default; set it to strict only while migrating (see below) -->
Table 8. The GSS-related configuration properties (defaults from Resources.gwt.xml, 2.13.1)
Property Default Effect

CssResource.enableGss

false

Every CssResource is compiled with the GSS generator. With @Source the compiler prefers a .gss file next to the .css of the same name

CssResource.conversionMode

off

strict, lenient or off. With enableGss true, a .css file is converted in memory to GSS first (strict: any CSS error fails the build; lenient: tries to repair it; off: a .css file is an error)

CssResource.gssDefaultInUiBinder

false

Makes GSS the default for every ui:style block that has no gss attribute

What differs from legacy CSS

The Developer Guide page "GSS vs CSS syntax" lists the differences; all of them below were compiled and the result printed.

Table 9. The same features in both syntaxes
Feature Legacy CSS GSS

Constants

@def small 1px; (any case)

@def SMALL 1px;; upper case. A @def whose name is not upper case gives only a compile warning (WARNING for invalid @def name coverWidth. We will ignore this.) and the constant is ignored. A coverWidth() accessor still reads COVER_WIDTH (the generator tries the upper-snake form of the method name)

Constant expressions

none

@def HALF mult(divide(100%, 6), 3);; floating-point results can be imprecise (the probe printed 50.00000001%)

Runtime substitution

@eval color a.B.method();

@def COLOR eval("a.B.method()"); or eval("...") directly in a value

Data resources

@url bg myImage;

@def BG resourceUrl("myImage"); or resourceUrl("...") in a value

Sprites

@sprite .star { gwt-image: "starIcon"; ... }

a normal rule with the property gwt-sprite: "starIcon";

Conditionals on a binding property

@if user.agent safari { }, @if locale en { }

@if (is("safari")) { }, @if (is("locale", "en")) { } (one argument means user.agent), with &&, ||, !, @elseif and @else

Boolean configuration property

@if myProp { }

@if (MY_PROP) { } (single-valued, value true or false)

Runtime condition

@if (a.B.flag()) { }

@if (eval("a.B.flag()")) { }; cannot be combined with && or ||, cannot initialise a constant, and a constant cannot be the condition

External classes

@external .foo, bar;

@external foo;; no dot, a wildcard in quotes: @external 'gwt-*';

RTL protection

@noflip { ... } block

/* @noflip */ comment immediately before a declaration (see "Right-to-left")

Mixins, functions, loops

none

@defmixin, @mixin, functions such as mult and divide, concat() (a GWT addition of 2.8.0) and @for loops; documented in the Closure Stylesheets project and in the 2.8.0 release notes, not in the Developer Guide

A GSS file of the Bookshelf probe, with every GSS construct of the table, compiled and ran in the same way:

@def COVER_WIDTH 96px;
@def ACCENT #1a5fb4;
@def LOW_COLOR eval("probe.client.Palette.lowStockColor()");
@def SPINE_URL resourceUrl("spineData");
@external legacyBanner;

@defmixin box(BORDER_COLOR) {
  border: 1px solid BORDER_COLOR;
  border-radius: 4px;
}

.card {
  width: COVER_WIDTH;
  @mixin box(ACCENT);
  margin-left: 8px;
  float: left;
}

.title { font-weight: bold; }
.low-stock { color: LOW_COLOR; }
.spine { background-image: SPINE_URL; }

.star {
  gwt-sprite: "starIcon";
  display: inline-block;
}

.pin2 {
  /* @noflip */
  left: 4px;
  padding-left: 3px;
}

@if (eval("probe.client.Palette.compact()")) {
  .card { padding: 2px; }
}

@if (is("safari")) {
  .title { -webkit-font-smoothing: antialiased; }
} @else {
  .title { font-smooth: always; }
}

The text the GSS generator wrote, with the same pretty style (data URLs shortened):

.GO1C0CD-b-c-probe-gss-GRes-BookCss-card{width:96px;border:1px solid #1a5fb4;border-radius:4px;margin-left:8px;float:left}
.GO1C0CD-b-i-probe-gss-GRes-BookCss-title{font-weight:bold}
.GO1C0CD-b-e-probe-gss-GRes-BookCss-low-stock{color:#c01c28}
.GO1C0CD-b-g-probe-gss-GRes-BookCss-spine{background-image:url(data:image/png;base64,...)}
.GO1C0CD-b-h-probe-gss-GRes-BookCss-star{height:16px;width:16px;overflow:hidden;background:url(data:image/png;base64,...) -0px -0px  no-repeat;display:inline-block}
.GO1C0CD-b-c-probe-gss-GRes-BookCss-card{padding:2px}
.GO1C0CD-b-i-probe-gss-GRes-BookCss-title{-webkit-font-smoothing:antialiased}

(The lines are shown on separate rows for readability; GSS minifies the whole text onto one line.)

Modern CSS in GSS

GSS is parsed by a CSS3-era parser, not by the browser, so a selector or value the parser does not know is a build error. The probe tried the CSS that the page on variables and media queries (CSS variables and media queries) and the layout panels use:

Table 10. What the 2.13.1 GSS parser accepted
Construct Result Detail

@media (max-width: 600px) { }, @keyframes, @font-face, @supports

accepted

supports and -moz-document are in the default CssResource.allowedAtRules

display: grid, calc(), clamp(), aspect-ratio, inset, gradients, transform

accepted

values the parser does not interpret are passed through

:not(.y), :nth-child(2n+1), ::after

accepted

a class inside :not() still needs an accessor or @external

@container (min-width: 400px) { }

error unknown @ rule, accepted after <extend-configuration-property name="CssResource.allowedAtRules" value="container"/>

the allow-list is the supported way to admit a new at-rule

custom properties (:root { --accent: #1a5fb4; }) and var(--accent)

parse error

Parse error in src/probe/err/e.gss at line 1 column 9, even with var in CssResource.allowedFunctions; use @def constants, or put custom properties in a plain style sheet outside CssResource

CSS nesting (.card { &:hover { } })

parse error

flatten the rules; GSS has its own mixins

:is(...), :where(...), :has(...)

parse error

the parser stops at the . inside the pseudo-class arguments

Because of those limits, a design system written in modern CSS (or output of Sass, see "Related pages") should stay a plain style sheet loaded with a link, while CssResource styles the parts that are written for GWT and need obfuscation, typed access or per-permutation rules.

Migrating from legacy CSS to GSS

The migration article offers two paths (a one-step migration for a small application, a gradual migration for a large one) and a conversion tool. The order below follows both, with the additions that the probes showed to be necessary.

flowchart TD a["Legacy CSS, enableGss false"] --> b["Turn on enableGss with conversionMode strict"] b --> c["Run Css2Gss on a folder or on the whole application"] c --> d["Point @Source at the gss files or keep the css names"] d --> e{"Any css file left?"} e -->|"yes, next folder"| c e -->|"no"| f["Delete the css files"] f --> g["conversionMode off, gssDefaultInUiBinder true"] g --> h["Pure GSS"]

Step by step

  1. Upgrade to a GWT release with GSS (2.8 or later) and make sure the host page uses a doctype (see the pitfalls on quirks mode).

  2. Add CssResource.enableGss = true and CssResource.conversionMode = strict to the module. From now on the compiler converts every remaining .css file in memory, so the application keeps building while you move files.

  3. Convert a folder with Css2Gss (commands below), which writes a .gss file next to every .css file that does not have one. Its classpath needs gwt-dev as well as gwt-user: with gwt-user.jar alone the tool failed with NoClassDefFoundError: com/google/gwt/core/ext/TreeLogger.

  4. Point @Source at the .gss file, or leave @Source("book.css") as it is: with enableGss true the compiler looks for a .gss file of the same base name first, so a converted file replaces the .css without touching Java code (checked: both files present, conversion off, the .gss was used). If one file in a @Source list has no .gss counterpart the compiler falls back to the originals, because a list must not mix the two syntaxes (Only either css files or gss files are supported on one interface).

  5. Review and fix the converted files (see "What the converter does"), build, and look at the page.

  6. When no .css file is referenced any more, delete the .css files and set CssResource.conversionMode to off (the default; remove the line). Set CssResource.gssDefaultInUiBinder to true once every ui:style block is GSS.

# Convert one file; the GSS goes to standard output
java -cp {classpath} com.google.gwt.resources.converter.Css2Gss src/main/resources/book.css

# Convert every .css under a directory next to the originals, recursively
java -cp {classpath} com.google.gwt.resources.converter.Css2Gss -r src/main/resources

# A file that uses @def constants defined elsewhere needs a scope
java -cp {classpath} com.google.gwt.resources.converter.Css2Gss -scope colors.css,sizes.css book.css

Here {classpath} stands for gwt-user-2.13.1.jar, gwt-dev-2.13.1.jar and their dependencies (the output of mvn dependency:build-classpath for the client module works).

The options printed by the tool in 2.13.1 are -r (recursive), -condition list_of_condition (variables that are configuration properties and should not be turned into is() calls) and -scope list_of_files. A file that already has a .gss neighbour is skipped (GSS file already exists - will not convert).

What the converter does

The converter wrote this GSS for the legacy book.css shown earlier (blank lines removed):

@def COVER_WIDTH 96px;
@def ACCENT #1a5fb4;
@def LOW_STOCK_COLOR eval('probe.client.Palette.lowStockColor()');
@def SPINE_URL resourceUrl("spineData");
@external legacyBanner;
.card { width: COVER_WIDTH; border: 1px solid ACCENT; margin-left: 8px; float: left; }
.star { gwt-sprite: "starIcon"; display: inline-block; }
.pin { /* @noflip */ left: 4px; }
@if (eval('probe.client.Palette.compact()')) { .card { padding: 2px; } }
@if (is("user.agent", "safari")) { .title { -webkit-font-smoothing: antialiased; } }
@else  { .title { font-smooth: always; } }
Table 11. Legacy construct and converter output
Legacy Converted

@def coverWidth 96px;

@def COVER_WIDTH 96px; (renamed to upper case; the original accessor int coverWidth() kept working in the strict-conversion build)

@eval lowStockColor ...;

@def LOW_STOCK_COLOR eval('...');

@url spineUrl spineData;

@def SPINE_URL resourceUrl("spineData");

@sprite .star { gwt-image: "starIcon"; ... }

.star { gwt-sprite: "starIcon"; ... }

@noflip { .pin { left: 4px; } }

.pin { /* @noflip */ left: 4px; }

@if (a.B.flag()) { }

@if (eval('a.B.flag()')) { }

@if user.agent safari { } @else { }

@if (is("user.agent", "safari")) { } @else { }

Known limits, from the article and the probes:

  • comments are lost: the old parser does not hand them to the converter, so re-add them by hand

  • a CSS hack such as *zoom: 1 is dropped with a warning ([WARN] Line 1 column 20: encountered "*". Was expecting one of: "}" ";" <IDENT>) and the build continues, so search the log for those warnings

  • a file that uses a @def defined in another file fails ("refuses to convert if it encounters an undefined variable"): pass -scope

  • the converter only supports strict mode and refuses broken CSS; conversionMode = lenient exists in the compiler to repair invalid CSS "of the whole application at once", but the article calls strict "the mode you want to be using"

  • the compiler’s own comment warns that automatic conversion "will be removed in the next version of GWT": treat strict and lenient as a bridge

Configuration combinations

Many error messages come from invalid combinations of the three properties. All rows below were produced by compiling the Bookshelf card template (a ui:style with a type and a @def) with the settings shown.

Table 12. Combinations and results
enableGss / conversionMode / gssDefaultInUiBinder ui:style Result

false / off / false

CSS body, no attribute

Works (the default)

false / off / false

gss="true"

UiBinder file has attribute gss="true", but GSS is disabled globally

false / off / true

no attribute

Invalid combination of configuration properties. CssResource.enableGss is false, but CssResource.uiBinderGssDefault is true (the message uses an old property name)

true / off / false

CSS body, no attribute

Your ClientBundle is referencing css files instead of gss. You will need to either convert these files to gss using the converter tool or turn on auto convertion in your gwt.xml file. ...

true / off / false

gss="true" and GSS body

Works

true / off / false

gss="false"

UiBinder file has attribute gss="false", but CssResource.conversionMode is "off"

true / strict / false

CSS body, no attribute

Works (converted in memory)

true / off / true

GSS body, no attribute

Works

An important consequence is visible in the SDK itself: NativeHorizontalScrollbar, used by DataGrid, CellTable and CustomScrollPanel, has a legacy CSS ui:style inside its template. With enableGss true and conversionMode left at off, a module that created a DataGrid failed with Errors in 'gen/com/google/gwt/user/client/ui/NativeHorizontalScrollbar_NativeHorizontalScrollbarUiBinderImpl.java' and the "referencing css files" message. Setting conversionMode to strict fixed it (this is why the Bookshelf module of the earlier pages carries strict), and so did gssDefaultInUiBinder = true: with off and that property a module with 19 widget classes (DataGrid, CellTable, CellList, CellBrowser, SimplePager, Tree, MenuBar, DateBox, DatePicker, SuggestBox, RichTextArea, SplitLayoutPanel, TabLayoutPanel, StackLayoutPanel, TabPanel, DisclosurePanel, CustomScrollPanel, DialogBox, PopupPanel) compiled, so the SDK’s inline CSS is also valid GSS. The pure-GSS configuration of the worked example is therefore enableGss true, gssDefaultInUiBinder true and no conversionMode.

Styles in UiBinder

ui:style writes CSS next to the markup that uses it; the UiBinder page explains the element and its attributes (field, src, type, gss, and the generator-only import). What matters for styling is scope: the compiler turns each ui:style block into a CssResource of its own, so .card in one template never collides with .card in another. The class names are written in attribute values as {style.card}, and a typo is a compile error rather than a silent no-op.

<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
             xmlns:g="urn:import:com.google.gwt.user.client.ui">
  <ui:with field="res" type="com.example.bookshelf.client.style.BookshelfResources"/>
  <ui:style type="com.example.bookshelf.client.style.BookCard.Style">
    @def GAP 8px;
    .card { margin: GAP; padding: GAP; border: 1px solid #c0bfbc; position: relative; }
    .title { font-weight: bold; }
    .selected { border-color: #1a5fb4; }
  </ui:style>
  <g:FlowPanel styleName="{style.card}">
    <g:SimplePanel styleName="{res.css.cover}"/>
    <g:Label ui:field="title" styleName="{style.title}"/>
    <g:Label ui:field="copies"/>
    <g:Button ui:field="lend" text="Lend"/>
  </g:FlowPanel>
</ui:UiBinder>
  • type names a CssResource subtype (a nested interface of the owner class) so Java can read the names through a @UiField Style style;; the interface must not require a name the CSS does not define (a ghost() accessor gave ghost: Fix by adding .ghost{})

  • styleName="{style.card}" replaces the widget’s class attribute; addStyleNames="{style.card} {style.hot}" (plural) adds to it. Use the second one on a widget whose theme class you want to keep

  • the global bundle is declared with ui:with and read as {res.css.cover} (the css() accessor), so one set of rules is shared by many templates, and the template’s own block holds what only that template needs

  • a misspelt name fails the build: {style.cardd} gave Could not find no-arg method named cardd in type probe.ub.BookCard.Style

  • ui:style is injected automatically when the template is first created, so you do not call ensureInjected() on it; a global bundle that you declared by hand still needs it, and the example does so in the constructor

The Java side reads the scoped names through the typed field:

public class BookCard extends Composite {
  interface Binder extends UiBinder<Widget, BookCard> {}

  /** Scoped to this template: the three names are obfuscated for this template only. */
  interface Style extends CssResource {
    String card();

    String title();

    String selected();
  }

  private static final Binder BINDER = GWT.create(Binder.class);
  private static final BookshelfResources RES = BookshelfResources.INSTANCE;

  @UiField Style style;
  @UiField Label title;
  @UiField Label copies;
  @UiField Button lend;

  public BookCard(String bookTitle, int copiesLeft) {
    RES.css().ensureInjected();
    RES.nav().ensureInjected();
    initWidget(BINDER.createAndBindUi(this));
    title.setText(bookTitle);
    copies.setText(copiesLeft + " copies");
    if (copiesLeft < 3) {
      copies.addStyleName(RES.css().lowStock());
    }
    lend.setEnabled(copiesLeft > 0);
  }

  public void setSelected(boolean selected) {
    setStyleName(style.selected(), selected);
  }
}

title.setText(...) and copies.setText(...) put the book data in as text, never as HTML; do the same for any value that comes from the server (the SafeHtml & XSS page covers the rest).

Right-to-left (RTL)

CssResource can generate the right-to-left variant of a style sheet at compile time. The variant is chosen at run time by LocaleInfo.getCurrentLocale().isRTL(), so it follows the module’s locale property (a language such as Arabic, ar, makes isRTL() true); the Internationalization page covers how locales are added. Adding a right-to-left locale doubles the permutations: the RTL probe compiled four permutations instead of two.

The Developer Guide lists the transformations: left and right swap, so do the values left/right of clear, float, text-align and the page-break-* properties; background positions in percent are mirrored; four-value margin, padding and border-* shorthands are reversed; xyz-right properties become xyz-left; direction is flipped on the body selector only; and ne-resize-style cursors are mirrored. A pixel-based background-position: 4px 10px is not flipped; for old browsers the guide suggests a sprite with left/top instead. Both syntaxes did the same in the probes:

Table 13. The card rule under ?locale=ar
Syntax Default locale Locale ar (isRTL() true)

Legacy CSS

margin-left : 8px; float : left;

margin-right : 8px; float : right;

GSS

margin-left:8px;float:left

margin-right:8px;float:right

To keep a rule as written, mark it noflip; the way depends on the syntax. The probe used a class that must always sit at the left edge:

/* legacy CSS: a block */
@noflip {
  .pin { left: 4px; }
}

/* GSS: a comment before the declaration */
.pin2 {
  /* @noflip */
  left: 4px;
  padding-left: 3px;
}

Under ar, the GSS output was .pin2{left:4px;padding-right:3px}: left stayed, the neighbouring padding-left flipped. Two ways to get it wrong, both silent unless you read the output:

  • the comment must sit before the declaration; placed before the rule (/* @noflip */ .pin3 { left: 4px; }) the GSS pass still flipped it (pin3{right:4px})

  • the legacy block form is not GSS: @noflip { ... } in a .gss file is unknown @ rule

ImageResource can be mirrored too, with @ImageOptions(flipRtl = true) on the bundle method (the ClientBundle & Images page covers it). The theme modules have an RTL version (DarkRTL, StandardRTL, CleanRTL, ChromeRTL); the probe linked gwt/dark/dark_rtl.css for DarkRTL, while the plain Dark module links dark.css.

Bookshelf: the stylesheet of the catalog

The running Bookshelf scenario of this guide gets its look in four files, all compiled and run in a scratch module (com.example.Shelf) with the Clean theme: a global GSS bundle, a small shared-state file, a UiBinder BookCard with a scoped style, and the module settings.

<module rename-to="shelf">
  <inherits name="com.google.gwt.user.User"/>
  <inherits name="com.google.gwt.user.theme.clean.Clean"/>
  <source path="bookshelf/client/style"/>
  <entry-point class="com.example.bookshelf.client.style.ShelfEntry"/>
  <extend-property name="locale" values="ar"/>   <!-- right-to-left, for the demonstration -->
  <set-configuration-property name="CssResource.enableGss" value="true"/>
  <set-configuration-property name="CssResource.gssDefaultInUiBinder" value="true"/>
</module>
/* bookshelf.gss: leaves the theme's own class names untouched so that they can be overridden */
@external 'gwt-*';

@def ACCENT eval("com.example.bookshelf.client.style.Theme.accent()");
@def LOW_STOCK #b03a2e;
@def COVER_WIDTH 72px;

@defmixin rounded(RADIUS) {
  border-radius: RADIUS;
}

.header {
  background-color: ACCENT;
  color: #fff;
  padding: 0.6em 1em;
}

.gwt-Button {
  background-color: ACCENT;
  color: #fff;
  border: 0;
  @mixin rounded(4px);
}

.gwt-Button[disabled] {
  opacity: 0.5;
}

.selected {
  background-color: #dbe9fa;
}

.low-stock {
  color: LOW_STOCK;
  font-weight: bold;
}

.cover {
  gwt-sprite: "coverPlaceholder";
  display: inline-block;
  float: left;
  margin-right: 8px;
}

.ribbon {
  position: absolute;
  /* @noflip */
  left: 0;
  top: 0;
}

@if (eval("com.example.bookshelf.client.style.Theme.compact()")) {
  .header { padding: 0.2em 0.5em; }
}
/* nav.gss: the second stylesheet of the bundle; .selected is shared with BookshelfCss */
.item {
  display: block;
  padding: 0.5em 1em;
  cursor: pointer;
}

.selected .item {
  font-weight: bold;
}

The Java types are the BookshelfCss, BookshelfResources and BookCard shown earlier, plus the shared and navigation types and a small class that supplies the run-time accent colour:

@Shared
public interface StateCss extends CssResource {
  String selected();
}

public interface NavCss extends StateCss {
  String item();
}

public final class Theme {
  private Theme() {}

  public static String accent() {
    return "#1a5fb4";
  }

  public static boolean compact() {
    return false;
  }
}

The module showed what the page claims. This is the output of the entry point after a deferred command (so that the injected style sheet is in place), in the default locale:

locale=default rtl=false
COVER_WIDTH=72
lowStock=EFLPHAC-a-d
css.selected=EFLPHAC-a-f nav.selected=EFLPHAC-a-f shared=true
button bg=rgb(26, 95, 180) radius=4px disabled-opacity=0.5
<div class="EFLPHAC-c-a EFLPHAC-c-b"><div class="EFLPHAC-a-b"></div><div class="EFLPHAC-c-c">Emma</div><div class="gwt-Label EFLPHAC-a-d">1 copies</div><button type="button" class="gwt-Button">Lend</button></div>

Reading it:

  • COVER_WIDTH=72 is the numeric constant read from Java

  • the card element has two classes (-c-a is card, -c-b the template’s selected), the title -c-c, while the global lowStock is -a-d: three scopes in one page, no collision

  • the Button class is still gwt-Button (@external 'gwt-*'), so the Clean rules and the override both match, and because the override is injected later in the document at the same specificity it wins: the button background changed from the theme’s transparent to rgb(26, 95, 180), and the disabled button got opacity 0.5 from .gwt-Button[disabled]

  • in the right-to-left locale the same bundle printed float:right;margin-left:8px for .cover and kept position:absolute;left:0 for .ribbon, as /* @noflip */ requires

  • in Super Dev Mode (port 9911) editing bookshelf.gss and requesting a recompile changed the result in about 0.3 seconds (@mixin rounded(10px) gave radius=10px after the reload) and the class names were readable (stable style)

Security notes

  • Never build CSS from user input. StyleInjector.inject(String) and CssResource text go into a style element. A value from a request must not become part of that text, because a declaration like background: url(...) can load a remote resource and make the browser send data, and CSS can be used to extract attribute values. Set user-controlled values on elements with the typed Style API only after validating them, and select user-controlled states with class names from accessors

  • GSS eval(...) (legacy @eval) runs a static Java method of your own code; the expression is part of the source, not of the request. Keep those methods free of side effects and sensitive state

  • obfuscation is not a security control: class names are short, but the style sheet is public

  • StyleInjector writes an inline style element, so a Content-Security-Policy with a strict style-src must allow it; the source of StyleInjector has no nonce support. Inlined images are data: URLs, so img-src must allow data:. The SafeHtml & XSS and XSRF, RPC & Server Security pages cover CSP in the whole application

  • when you keep a style sheet outside GWT (a link to a third-party theme), its origin is part of your trust boundary; use Subresource Integrity if it is on a CDN

Pitfalls

Each row was reproduced with the 2.13.1 compiler (module -strict), unless it says it comes from the documentation.

Table 14. Pitfalls and exact messages
Message or symptom Cause Fix

The following non-obfuscated class is present in a strict CssResource: unmapped. Fix by adding String accessor method(s) to the CssResource interface for obfuscated classes, or use an @external declaration for unobfuscated classes. (GSS)

a class in the file has no accessor

add String unmapped();, or declare it @external

The following obfuscated style classes were missing from the source CSS file: …​ missing: Fix by adding .missing{}, then CssCompilerException: Missing a CSS replacement (legacy)

an accessor has no class in the file

add .missing {} or remove the accessor

The following method [missing()] doesn't match a constant nor a style class. You could fix that by adding .missing {} (GSS)

the same, in GSS; it also hits @Shared accessors that one file leaves out

add an empty rule for the shared class

The following non-obfuscated class is present in a strict CssResource: low-stock. (GSS) or The following unobfuscated classes were present in a strict CssResource: / low-stock (legacy)

a dashed class name and an accessor lowStock(); the accessor binds to .lowStock

@ClassName("low-stock") on the accessor

Your ClientBundle is referencing css files instead of gss. You will need to either convert these files to gss using the converter tool or turn on auto convertion in your gwt.xml file.

enableGss is true and a .css file is used with conversionMode off, in your bundle or in the SDK (DataGrid and NativeHorizontalScrollbar_NativeHorizontalScrollbarUiBinderImpl_GenBundle)

convert the file; for the SDK set conversionMode to strict or gssDefaultInUiBinder to true

UiBinder file has attribute gss="true", but GSS is disabled globally

gss="true" while enableGss is false

enable GSS or drop the attribute

Invalid combination of configuration properties. CssResource.enableGss is false, but CssResource.uiBinderGssDefault is true

gssDefaultInUiBinder without enableGss (the message names the property uiBinderGssDefault)

enable GSS first

UiBinder file has attribute gss="false", but CssResource.conversionMode is "off"

a ui:style gss="false" block after conversion is off

convert the block to GSS and remove the attribute

Only either css files or gss files are supported on one interface

@Source({"a.css", "b.gss"})

one syntax per accessor

Error in src/probe/err/e.gss[line: 1 column: 1]: unknown @ rule

a legacy block such as @noflip { ... } in a .gss file (also @container before allowedAtRules)

use /* @noflip */ before a declaration; add the at-rule to CssResource.allowedAtRules

Unable to parse CSS / Parse error in src/probe/err/e.gss at line 1 column 29 (Encountered " ")" ") "")

a legacy @eval name call(); or other legacy directive in GSS, or --custom properties, var(), nesting, :has(), :is(), :where()

eval("call()"); use @def; flatten nesting

Parse error ... Encountered " "." ". ""

@external .foo; with a dot in GSS

@external foo;

Error in src/probe/err/e.gss[line: 1 column: 16]: GSS constant not defined: BIG_WIDTH

a constant is used but not defined in GSS; legacy CSS accepted the same file and left bigWidth in the value

define it, or re-check the spelling

WARNING for invalid @def name coverWidth. We will ignore this.

a lower-case @def in GSS: a warning only, the rule that uses it is not correct

use upper-case names

Could not find no-arg method named cardd in type probe.ub.BookCard.Style

{style.cardd} in a template with a typed ui:style

fix the name; the compiler is protecting you

a /* @noflip */ before a rule has no effect in RTL

the comment applies to the next declaration

put it before the declaration

a @Shared accessor returns a different name in a ui:style than in a bundle

UiBinder generates its own bundle per template

apply shared state classes from one hand-written bundle

Annotation error: expected class java.lang.Class, got null, then NullPointerException: Cannot invoke "java.lang.Class.getName()" because "clazz" is null

@Import(X.class) with X only on the source path, not compiled

run the compiler with target/classes on the classpath

the override style has no effect right after ensureInjected()

StyleInjector flushes at the end of the event loop

read styles in a deferred command

NoClassDefFoundError: com/google/gwt/core/ext/TreeLogger when running Css2Gss

only gwt-user.jar on the classpath (the Developer Guide shows that)

add gwt-dev.jar

styles are lost, or .A and .a clash, in a page without <!doctype html>

the migration article: GSS uses upper and lower case in class names and "running your gwt host page in quirks mode will make GSS fail" (not reproduced; the observed names such as GO1C0CD-b-c mix both cases)

use <!doctype html> in the host page

Where older material is outdated

  • The Developer Guide, CSS page. It lists three themes (Standard, Chrome and Dark) and says new applications use Standard; the source has a fourth, Clean, which the archetype uses, and the guide’s text predates Clean. It also calls the link and <stylesheet> approaches "Deprecated".

  • The Developer Guide, ClientBundle page. Its CssResource section is the legacy syntax: ie6, ie8, opera in @if user.agent blocks and -standalone for the interface generator are historical, and the guide’s remark that the obfuscation prefix is an "Adler32 checksum of the source css file expressed in base36 as a prefix (7 chars)" does not match the names observed (ten characters such as GCWXV1BCHI in legacy mode; in GSS a seven-character prefix such as GO1C0CD, derived from the CssResource types in the source, plus a counter such as b-c).

  • The book (2007). Chapter 3 teaches module <stylesheet>, a Dashboard.css next to the module, setStyleName and the default widget style names (it points to a web page that listed the standard names). That still works, and the naming conventions it recommends (a primary style name per widget and dependent names for state) are the ones the SDK uses today; but there was no CssResource, ClientBundle, UiBinder ui:style, GSS, themes or RTL flipping.

  • Experimental flags. Articles and posts that mention CssResource.legacy (2.7.0) describe the experimental stage; CssResource.conversionMode replaced it.

What changed recently

Version Change

2.7.0

Experimental GSS: CssResource.enableGss and gss="true" on ui:style

2.8.0 (Beta1)

GSS no longer experimental; the old CssResource syntax deprecated; default still CSS. A for-loop, concat() and @gen-webkit-keyframes are added

2.8.0 RC1 and RC3

InterfaceGenerator reads CSS3 and GSS (RC1); generation of accessors for GSS constants fixed (RC3)

2.13.1

Default is still CSS (CssResource.enableGss false); GSS parser accepts CSS3 constructs but not CSS custom properties or nesting; the compiler’s own comment says automatic conversion is to be removed