|
CssResource
Compile-time CSS processing
ImmutableResourceBundle and CssResource have been promoted from the incubator into mainline GWT development. The incubator code has been deprecated. Current documentation is located in the main GWT project.
See also the CssResourceCookbook Goals
Non-Goals
Overview
FeaturesConstants(Working) @def small 1px; @def black #000; border: small solid black;
interface MyResources extends CssResource {
int small();
}
Runtime substitution(Working) @eval userBackground com.module.UserPreferences.getUserBackground();
div {
background: userBackground;
}
Value function.myDiv {
offset-left: value('imageResource.getWidth', 'px');
}
@def SPRITE_WIDTH value('imageResource.getWidth', 'px')
.selector {
width: SPRITE_WIDTH;
}Conditional CSS(Working) /* Runtime evaluation in a static context */
@if (com.module.Foo.staticBooleanFunction()) {
... css rules ...
}
/* Compile-time evaluation */
@if <deferred-binding-property> <space-separated list of values> {
... css rules ...
}
@if user.agent safari gecko1_8 { ... }
@if locale en { ... }
/* Negation is supported */
@if !user.agent ie6 opera {
...
}
/* Chaining is also supported */
@if (true) {
} @elif (false) {
} @else {
}
Image Sprites(Working): @sprite .mySpriteClass {gwt-image: "imageAccessor"; other: property;} => generates =>
.mySpriteClass {
background-image: url(gen.png);
clip: ...;
width: 27px;
height: 42px;
other: property;
}class MyCssResource extends CssResource {
String mySpriteClass();
}
class MyResources extends ImmutableResourceBundle {
@Resource("my.css")
MyCssResource css();
@Resource("some.png")
ImageResource imageAccessor();
@Resource("some.png")
@ImageOptions(repeatStyle=RepeatStyle.Horizontal)
ImageResource repeatingImage();
}
References to Data Resources(Working) @url myCursorUrl fancyCursorResource;
.myClass {
cursor: myCursorUrl, pointer;
}
RTL support(Working)
@noflip {
.selector {
left: 10;
}
}
background-position: left 4px top 10px; @sprite .bgImage {
gwt-image: 'background-image';
position: absolute;
left: 4px;
top: 10px;
}@Resource("icon128.png")
@ImageOptions(flipRtl = true)
ImageResource logo();Selector obfuscation(Working): java:
class Resources {
MyCSSResource myCSSResource();
}
class MyCSSResource extends CSSResource {
Sprite mySpriteClass();
String someOtherClass();
String hookClass();
}
myWidget.addStyleName(resource.mySpriteClass());
css:
@sprite mySpriteClass mySpriteImage;
.someOtherClass {
/* ... */
}
.hookClass{} /* Empty and stripped, but left for future expansion */
Unplanned use casesThese use cases are not planned, but remain here for the sake of completeness. Compile-time import@import cssResourceFunctionName;
Conclusion: too confusing. The modularity afforded by CSS's @import statement is inherent in the CssResource design. Implementation fix-ups(Not planned): div:hover { }
Could use top-level event handler to fake on IE
div:focus { }
Problematic -- focus/blur don't bubbleConclusion: technically possible, but likely unaffordable at runtime. The rendering engines that don't provide the wanted features in the first place likely don't provide the means to implement those missing features efficiently. OptimizationsBasic minificationBasic minification of the CSS input results in the minimum number of bytes required to retain the original structure of the input. In general, this means that comments, unnecessary whitespace, and empty rules are removed. .div {
/* This is the default background color */
background: blue;
}
.empty {}would be transformed into .div{background:blue;}Selector mergingRules with identical selectors can be merged together. .div {prop: value;}
.div {foo: bar;}becomes .div {prop:value;foo:bar;} However, it is necessary that the original semantic ordering of the properties within the CSS is preserved. To ensure that all selector merges are correct, we impose the restriction that no rule can be promoted over another if the two rules define a common property. We consider border and border-top to be equivalent properties, however padding-left and padding-right are not equivalent. Thus .a {background: green;}
.b {border: thin solid blue;}
.a {border-top: thin solid red;}cannot be merged because an element whose CSS class matches both .a and .b would be rendered differently based on the exactly order of the CSS rules. When working with @if statements, it is preferable to work with the form that operates on deferred-binding properties because the CSS compiler can evaluate these rules statically, before the merge optimizations. Consider the following: .a {
background: red;
}
@if user.agent safari {
.a {
\-webkit-border-radius: 5px;
}
} @else {
.a {
background: url('picture_of_border.png');
}
}In the safari permutation, the rule becomes .a{background:red;\-webkit-border-radius:5px;} while in other permutations, the background property is merged. Property mergingRules with identical properties can be merged together. .a {background: blue;}
.b {background: blue;}can be transformed into .a,.b{background:blue;}Promotion of rules follows the previously-established rule of not promoting a rule over other rules with common properties. Levers and Knobs
Selector obfuscation detailsScopeScoping of obfuscated class names is defined by the return type of the CssResource accessor method in the resource bundle. Each distinct return type will return a wholly separate collection of values for String accessor methods. interface A extends CssResource {
String foo();
}
interface B extends A {
String foo();
}
interface C extends A {
String foo();
}
interface D extends C {
// Intentionally not defining foo()
}
interface Resources {
A a();
A a2();
B b();
C c();
D d();
D d2();It will be true that a().foo() != b().foo() != c().foo() != d().foo(). However, a().foo() == a2().foo() and d().foo() == d2().foo(). Shared scopesIn the case of "stateful" CSS classes like focused or enabled, it is convenient to allow for certain String accessor functions to return the same value, regardless of the CssResource type returned from the accessor method.
@Shared
interface FocusCss extends CssResource {
String focused();
String unfocused();
}
interface A extends FocusCss {
String widget();
}
interface B extends FocusCss {
String widget();
}
interface C extends B {
// Intentionally empty
}
interface Resources {
A a();
B b();
C c();
FocusCss f();
}In this example, a().focused() == b().focused() == c().focused == f().focused(). However, a().widget() != b().widget != c.widget(), as in the previous example. The short version is that if distinct CSS types need to share obfuscated class names, the CssResource subtypes to which they are attached must share a common supertype that defines accessors for those names and has the @Shared annotation. Imported scopesThe Java type system can be somewhat ambiguous when it comes to multiple inheritance of interfaces that define methods with identical signatures, although there exist a number of cases where it is necessary to refer to multiple, unrelated CssResource types. Consider the case of a Tree that contains Checkboxes. @ImportedWithPrefix("tree")
interface TreeCss extends CssResource {
String widget();
}
@ImportedWithPrefix("checkbox")
interface CbCss extends CssResource {
String widget();
}
interface MyCss extends CssResource {
String other();
}
interface Resources {
@Import({TreeCss.class, CbCss.class})
MyCss css();
}/* Now we can write a descendant selector using the prefixes defined on the CssResource types */
.tree-widget .checkbox-widget {
color: red;
}
.other {
something: else;
}Composing a "TreeCbCss" interface would be insufficient because consumers of the TreeCss interface and CbCss interface would receive the same value from the widget method. Moreover, the use of just .widget in the associated CSS file would also be insufficient without the use of some kind of class selector prefix. The prefix is defined on the CssResource type (instead of on the CssResource accessor method) In the interest of uniformity across all CSS files that import a given scope. It is a compile-time error to import multiple classes that have the same prefix or simple name. The case of shared scopes could be handled solely with importing scopes, however this form is somewhat more verbose and relationships between unrelated scopes is less common than the use of stateful selectors. Example: StackPanel inside a StackPanelThis is a use-case that is currently impossible to style correctly in GWT. // Assume this interface is provided by the UI library
interface StackPanelCss extends CssResource {
String widget();
// and many more class names
}
// App code defines the following interfaces:
@ImportedWithPrefix("inner")
interface StackPanelInner extends StackPanelCss {
// Empty interface
}
interface StackPanelOuter extends StackPanelCss {
// Empty interface
}
interface Resources {
@Resource("stackPanel.css")
@Strict
StackPanelInner inner();
@Import(StackPanelInner.class)
@Resource("stackPanel.css", "outer.css")
@Strict
StackPanelOuter outer();
}The file stackPanel.css defines the basic structure of any given stackPanel: .widget .title {}
.widget .content {}
/* Other stuff to make a StackPanel work */The outer() method can continue to use the base stackPanel.css file, because the accessor methods defined in StackPanelCss are mapped into the default (no-prefix) namespace. The inner StackPanel's style members are also available, but in the inner prefix. Here's what outer.css might contain: .widget {color: red;}
.inner-widget {
color: blue;
font-size: smaller;
}Strict scopingIn the normal case, any class selectors that do not match String accessor functions are left unobfuscated in the compiled output. interface MyCssResource extends CssResource {
String foo();
}
interface Resources {
@Strict
@Resource("my.css")
MyCssResource css();
}/* This is ok */
.foo {}
/* This would generate a compile error in @Strict mode */
.other {}The @Strict annotation can be applied to a CssResource accessor function to make it a compile-time error to have any unobfuscated class selectors in the CSS file. This additional level of restriction is recommended for library-oriented resource bundles in order to avoid inadvertently polluting the global CSS namespace. Strict scoping can be forced on for all CssResources by adding <set-property name="CssResource.forceStrict" value="true" /> to the module XML file, however this is only recommended for use by applications or test modules and never in any module that will be redistributed. Important open questions
|
Sign in to add a comment
Interesting and happy that the side line talk I heard somewhere about CSS optimization as a part of GWT compilation is coming to life.
Does this cover the following use cases? (I also minimally checked out, http://code.google.com/p/google-web-toolkit-incubator/wiki/UiBinder)
The developer has one java object which s/he have to show in more than one different UI. The fields and data behavior will remain same in different GUI But s/he have a good HTML/CSS designer who can generate more than one static screen. The developer would just cut and slice the HTML, and insert ids in right places Then use something like HTMLPanel to insert the dynamic server-bound GWT components/composites inside this designer provided layout (with CSS), by id(?). The developer wants the option of upfront or the lazy loading of the layouts in runtime Of course with the hashed names to avail the browser cache, and still dynamic updates (like normal gwt html/js). While doing this, it would be nice if the CSS is 'validated' as much is possible from static information, and also unified (clubbing many css file), and also minified. The developer would just like to annotate the basic java class, and turn it into a GUI for the given layout (name based mapping with compiler and extra annotation to check).
How much can the new UiBinder and CssResource direction help in this? Though this looks like a complex use case, it was straight for me as I can see that a graphics designer generates better static HTML layout than me. So I would like to off load all my layout concerns and concentrate only on component and server interaction.
I practically and crudely did this the first time with HTMLPanel, and rpc to fetch the html template. But surely that was not optimal and too much boiler plate code, also not hashed and cached.
Now I'm considering generator to get it this way. As I see it is few days work (need to learn with the good 1.5 showcase example for generator). The benefits are.
# CSS can be CSS and stay declarative, unlike some IF I see above (not sure if that is unavoidable) # Normal flow, and anyway required as a the lowest denominator.
Would love some feedback on how I can use the current time better, in anticipation of an effective solution for the above.
Oh sorry, I messed the formatting. Why is still wiki like formatting used, why not rich editor as in GWT?
A feature request:
Provide some API to expose @defs in Java code. Many apps that needs to dynamically alter CSS (for animation, for state changes, hide/show, etc..) will probably need access to CSS property values. Redefining UI constants in Java code (and thus having to maintain these constants in both Java and your CSS file) is yucky. You have essentially created a mechanism to define CSS variables in CSS and it would be awesome if that could be exposed in code :).
The usage might looks like the following: MyCss?.css:
.foo { height: 20px; }While the above API seems "nice", some Issues that jump out at me are: what type should be returned by foo().height() (or foo.<any Property>? You could have a type for each class of CSS property, namely Pixel, Color, String, but I think you could go even simpler and just return String/int.
How can the caller know what properties have been set for a specific CSS selector? What should be returned when querying the height of something which has not had the height property specified? A simple solution would be to adopt the familiar foo().getPropertyString() / foo().getPropertyInt() style of querying CSS properties. This would behave exactly like the equivalent dom element methods.
The above example would then become something like:
int height = resources.myCss().foo().getPropertyInt("height");very good article