Foundation
Project Documentation

About Skinning

A skin in Trinidad is a global style sheet that affects the entire application. You can create one skin for the entire application and as long as you have the <tr:document> tag on your page it will get picked up. Every component will automatically use the styling as described by the skin. Any changes to the skin will be picked up at runtime, no change to code is needed. Skins are based on the Cascading Style Sheet (CSS) specification. With a custom skin you can change a component's default css styles, like color, font, background-images, you can change images that are rendered with <img> tags, you can change text using a resource bundle, and if the component allows you can change component properties, like showing the last breadcrumb or not for the entire application instead of having to set an attribute on every instance of the component. Each component has skinning 'hooks'. The skinning hooks, aka keys or selectors, define what pieces of a component you can skin. There are also selectors that affect all the components, like a global background color or foreground color or a global font style. A Skin's stylesheet is more than just a stylesheet that contains CSS styles. It can also contain images that get rendered as <img>tags in your page. You can create skin information for a particular agent or reading direction all in the stylesheet. All this gets automatically transformed into the appropriate CSS-2 page and rendered component (the icons you define in the skin stylesheet do not get output to the generated CSS-2 file, but instead get used by the renderer). You can also dynamically switch skins during runtime, so you can create a skin with big fonts, a skin that is purple and pink, a skin that is blue and gold, and switch that depending upon locale or user or whatever you want. Put simply, with skinning you change the look of your application. Here is a visual example of how you can change the look of the navigationPane hints='tabs' component. There are three different looks for the tabs, and there are three different skins that provide these looks; one is the out-of-the-box skin and the others are custom skins created for demo purposes.

skinned tabs

Create a skin - an overview

Let's get started and create a very simple skin so that you can see what files you need and where you need to put them. Later we'll go over more in depth features of skinning, like @agent support and icons vs styles vs properties. This is what most people do when they work with skinning. This includes some tips and tricks:
  • Create a skin
    • Create a trinidad-skins.xml file and put it in your WEB-INF or META-INF of a jar file.
    • Create a *.css stylesheet document that gets linked to your skin definition in trinidad-skins.xml
    • Set trinidad-config.xml <skin-family> to be the skin-family of your skin.
    • Optionally, if the skin has a version, set trinidad-config.xml <skin-version> to be the skin-version of your skin, or "default" if you want choose the skin marked to be the default for that skin family.
  • Turn off styleclass name compression in web.xml (while creating the skin only then turn it back on for performance sake)
  • Turn on check file modification in web.xml (while creating the skin only then turn it back off for performance sake)
  • Install Firebug to help look at the DOM and the styleclasses
  • Run a component or application to see the default mode. Your skin will extend the default skin, unless you specify otherwise (because you can extend any skin you want).
  • Read the skin-selectors.html documentation for that component and also the :aliases (global selectors that affect more than one component, like text, background-color).
  • Cross-reference the skin-selectors documentation with Firebug in case the renderer and the documentation have gotten out of sync (this shouldn't happen, and if it does, please log a JIRA-issue/bug)
  • Decide what changes you want to make and add the Skin selectors/properties to your stylesheet.
  • By default you'll need to restart your server to see your skin changes. To see your changes without restarting your server, set the web.xml parameter 'org.apache.myfaces.trinidad.CHECK_FILE_MODIFICATION' to true. However, you always need to restart the server to see skin property changes.
  • Run your component to see the changes. NOTE: A frequently asked question is why don't I see my skin? The most frequently correct answer is that you forgot to add <tr:document> to your page. This kicks off the skin framework to create the skin's css file and link it to your page.

How to create a skin

To keep the first skin example simple, this section will explain what what files you need to create and change to create a bigfont skin. The bigfont skin does nothing more than change the default font for your application to be big, like 16px.

Create a trinidad-skins.xml file

Create a file in your WEB-INF directory and name it trinidad-skins.xml. Alternatively, you can create this file in the META-INF directory of your jar file and it will be found if that jar is on your application's classpath.
          
          <?xml version="1.0" encoding="ISO-8859-1"?>

            <skins xmlns="http://myfaces.apache.org/trinidad/skin">
                <skin>
                    <id>
                        bigfont.desktop
                    </id>
                    <family>
                        bigfont
                    </family>
                    <!-- optional. If you want to version your skin, then you set the version.
                    This is useful if you want to keep the skin-family the same name, but you
                    have fixed bugs in the skin. The user can use the original version or the 
                    new version by specifying skin-version in trinidad-config.xml.-->
                    <version>
                      <name>v1</name>
                      <default>true</default>
                    </version>
                    <!-- if you want this skin to be for a pda, use org.apache.myfaces.trinidad.pda as the render-kit-id -->
                    <render-kit-id>
                        org.apache.myfaces.trinidad.desktop
                    </render-kit-id>
                    <style-sheet-name>
                        skins/bigfont/bigfont.css
                    </style-sheet-name>
            <!-- we are extending the default, simple skin, so no need for this
                    <extends>xyz</extends>
            -->
            <!-- no need for this in our bigfont skin
                    <bundle-name>
                        org.apache.myfaces.trinidaddemo.resource.SkinBundle
                    </bundle-name>
            -->
                </skin>
            ... more skins could go here ...
            </skins>
            
The trinidad-skins.xml file is where you define your skins. Here are the attributes of a skin that you can define and their meaning:
  • id - Each skin must have an id. This is a unique identifier for a skin. The syntax we use is bigfont.desktop. We put the .desktop in there to specify that this is for the desktop renderkit. You can also create a skin for the pda renderkit if you would like.
  • family - Each skin must have a family. A skin belongs to a family and this is what you set in the trinidad-config.xml file under <skin-family>. Then the specific skin is chosen depending upon the renderkit that is being used on render. You can create a bigfont.pda skin that is also in the bigfont family. Then if a person is running your app with a pda, they would get the bigfont.pda skin. Otherwise, if you didn't have a bigfont.pda skin, they'd get the default skin which is simple.pda.
  • version - Version is optional. Setting a version on your skin is useful if you tend to change your css to fix bugs. Instead of creating a new skin and updating the skin-family so the end user can choose the new skin (e.g., purple-release2), you can instead create a new skin with the same skin-family, and a version like release2. This way the skin-family in trinidad-config.xml will not have to change. The user can either change the skin-version or they can set it to "default" You can set the name element of the version, and optionally you can set the default element to true/false. The default skin will get picked if the trinidad-config.xml only specifies the skin-family.
  • render-kit-id - The renderkit that this skin is designed for. The values can be org.apache.myfaces.trinidad.desktop or org.apache.myfaces.trinidad.pda.
  • style-sheet-name - This is your skin's stylesheet name url. We try a few different means to get the style-sheet-name file as an URL object.

    First, we try to get an URL for the non static urls, that is, urls that could change after the server has started:

    1. If style-sheet-name starts with "http:", "https:", "file:", "ftp:", or "jar:", then we create the URL by calling new java.net.URL(style-sheet-name).
    2. Else we create the URL by calling FacesContext's ExternalContext's getResource(style-sheet-name). (we prepend '/' if it isn't already there). This is how we find the style-sheet-name of the form "skins/bigfont/bigfont.css" when the file is in the application's project.
    If we still don't have an URL, we try to create the style-sheet-name URL using the ClassLoader's getResource. This is how we find a file in a jar when you have something like style-sheet-name= "META-INF/purpleSkin/styles/myPurpleSkin.css".
  • extends - This is how you can extend another skin rather than the default simple skin. Say you like the purple skin but only want to change the font size. You'd extend the purple.desktop skin and change one selector in the css file to override the font size.
  • bundle-name - This is the package where the skin's resource bundle lives. Skinning text is not recommended because you'll need to get the text translated for all the languages.
            <bundle-name>org.apache.myfaces.trinidaddemo.resource.SkinBundle
            </bundle-name>
            
  • translation-source - This is an EL binding that can point to a Map or a ResourceBundle. You can use this instead of the bundle-name if you would like to be more dynamic in your skin translations at runtime. bundle-name takes precedence.
            <translation-source>#{skinTranslationMap.contents}</translation-source>
            
            

Create your skin's stylesheet

In the previous step you created a trinidad-skins.xml which will create your skin. You also set the style-sheet-name to skins/bigfont/bigfont.css In your project directory, create the bigfont.css file to match the directory structure you specified in trinidad-skins.xml:
?- skins
?--- bigfont
?--?--- bigfont.css
We want to change the font to be bigger. Look in the skin-selectors.html doc for an appropriate selector to do this. Look for :alias selectors because aliases are 'global' and used in more than one component selectors. Thus alias selectors are a quick and easy way to change a style for your application rather than by each component selector. Here are the font alias selectors. .AFDefaultFontFamily:alias ? Specifies the default font family list ("font-family" property) for the skin. .AFDefaultFont:alias ? Specifies the default font for the skin. This style defines both the default font family (as specified by the AFDefaultFontFamily named style), the default font size, and default font weight. In bigfont.css, to change the font for your application, do this:
.AFDefaultFontFamily:alias {font-family: Tahoma}
.AFDefaultFont:alias {font-size: 16px}
Components that use font and font-family have included these aliases in their skin definitions, so if you change the aliases, everything that includes these aliases will change.

In trinidad-config.xml set the skin-family and optionally the skin-version

Open the trinidad-config.xml file that is in your WEB-INF directory. Set <skin-family>bigfont</skin-family> This will set your skin to bigfont when you run your application. You can EL-bind the skin-family as well to dynamically change the skin at runtime. If the skin has a particular version you want, you can also set the skin-version element to the name of the skin's version. Remember, your page must have the <tr:document> tag on the page to kick off the skin framework. Now you have created your bigfont skin. You can run your page and you will see that the font size and font family are now different than they were with the default simple.desktop skin.

Skinning a component - a step-by-step example

In the previous sections we discussed creating a skin that changes the font of your application. In this section, you'll learn how to skin an individual component. You'll need to read the skin-selectors documentation, and use tools like Firebug to figure out what to do when you have problems. The component we'll skin for this example is the panelBox component. First, familiarize yourself with the component. Run it with styleclass compression disabled, and view it with Firefox so you can get a feel for its dom structure. Look at the skin-selectors documentation for the available public skinning keys. We recommend against using html elements in your skin selectors because the rendering could change. Sticking with public skinning keys will lessen the chance that your skin will have to change if the renderer changes. From reading the documentation and viewing the panelBox component demo, you'll see that the panelBox has an attribute "background" that can be transparent, light, medium, dark. The panelBox's skin selectors are af|panelBox::transparent, af|panelBox::light, af|panelBox::medium, af|panelBox::dark when the background=transparent, light, medium, dark respectively. These selectors render on the root dom element as the documentation states. You want to change the panelBox from the simple skin look to the purple look:

panelBox purple skin

Since you only want to change the medium panelBox, you need to use descendent selectors and have the parent selector be af|panelBox::medium. You can see that the body is pink, so in your skin's css file you add this:
af|panelBox::medium af|panelBox::body {
  background-color: pink;
}
And you'll see that you still need some padding, so you'll add padding:
af|panelBox::medium af|panelBox::body {
  padding: 6px;
  background-color: pink;
}
Then you can view the panelBox with these changes. It is obvious what you want to do to the header. You want the text bold for all headers. And for the medium header you want the background to be aqua.
af|panelBox::medium af|panelBox::header {
  background-color: Aqua;
}


/* for all panelBox headers, make the font bold */
af|panelBox::header {
  font-weight: bold;
  padding: 2px;
}
You can run the panelBox demo with your skin-family set to see the change. If you have 'org.apache.myfaces.trinidad.CHECK_FILE_MODIFICATION' set to true, there is no need to restart the server to see css property changes .You can just refresh your page and the skinning framework will detect the skin has changed and will regenerate it. To see skin property changes, you always have to restart the server. Now you have this:

panelBox skin step one

You see that the content part of the panelBox needs a different color background and a dashed border and maybe some padding. You add this to your css file:
af|panelBox::medium af|panelBox::content {
  background-color: #E7E4EA;
  border-color: purple;
  border-style: dashed;
  border-width:1px;
  padding-right: 6px;
  padding-left: 6px;
}
And this gives you:

panelBox skin step two

Now you need the borders around the sides of the panelBox and the curved top-left and top-right. You add this:
/* rounded corners on the top-start and top-end */
af|panelBox::medium af|panelBox::top-start,
af|panelBox::medium af|panelBox::top-end:rtl {
  background-image: url(/skins/purple/images/panelBoxStart.gif);
  width:8px; 
  height:8px
}

af|panelBox::medium af|panelBox::top-end,
af|panelBox::medium af|panelBox::top-start:rtl {
  background-image: url(/skins/purple/images/panelBoxEnd.gif);
  height: 8px;
  width:  8px;
}

af|panelBox::medium af|panelBox::top {
  background-color: purple;
}

/* make the bottom have a background color and some padding, no rounded corners */
af|panelBox::medium  af|panelBox::bottom-start, 
af|panelBox::medium af|panelBox::bottom-end,
af|panelBox::medium af|panelBox::bottom {
  background-color: purple;
  padding-top: 8px;
}

af|panelBox::medium af|panelBox::start,
af|panelBox::medium af|panelBox::end {
  background-color: pink;
}
And this gives you your final panelBox that you wanted.

panelBox purple skin

Let's say for all panelBoxes other than medium, you want a background-color of yellow for the body. You can either do this:
af|panelBox::transparent af|panelBox::body,
af|panelBox::light af|panelBox::body,
af|panelBox::dark af|panelBox::body {
 background-color: yellow; 
}
or use an alias that is included in all the definitions above:
/* for all body, if doesn't have medium set, use yellow for the background-color */
/* The af|panelBox::medium af|panelBox::body {} definition above will take precedence over this alias 
so the medium panelBox's body will be pink, not yellow */
.AFPanelBoxBody:alias {
 background-color: yellow; 
}

Skinning Keys

This section is to help you understand the different kinds of skinning keys: component-level skinning keys, alias skinning keys, state skinning keys, skinning properties, and icon skinning keys. Other names for a skinning 'key' is skinning 'selector' or skinning 'hook'. This section will go over the skinning key syntax and their use. It is highly recommended that you familiarize yourself with the W3c's CSS specification regarding selectors, pseudo-elements and pseudo-classes. With this knowledge, skinning will be much easier.

Component-level skinning keys

This section will explain the syntax of a component-level skinning key and the pseudo-elements. In css, if you want to style a P element, you do the following:

P {color: red }

Similarly, if you want to style the af:inputText component using a skin selector, you would do this:

af|inputText {color:red }

The pseudo-element syntax is used to style pieces of a component. From the css spec, here is an example of how to skin the first line of a 'p' element:

p::first-line { text-transform: uppercase }

The Trinidad components also have pieces to them, like label, content, start-top, etc, and they are skinned using the appropriate pseudo-element defined in the skin-selectors document. Here is an example of some of the pseudo-element skin selectors for inputText.
  • af|inputText {...} - the entire component. renders on the root dom element
  • af|inputText::label {} - the label
  • af|inputText::content {} - the content (input element)
Note: If you are creating your own selectors you need to know that selector names that end in '-icon' or 'Icon:alias' become Icon Objects. Name selectors that are 'style' selectors something that does not end in 'icon', like 'icon-style'.

Alias skinning keys

Why do we need alias keys? Let's say you have a css-2 stylesheet for your page. You might define a bunch of style classes, and you might have font-style: Tahoma as your font-style of choice for all the style classes that are used for setting font. You might see a stylesheet like this:
    .AFInstructionText {font-family:Tahoma, Verdana, Helvetica, sans-serif;font-weight:normal;font-size:11px;color:#000000}
    .AFInstructionTextDisabled {font-family:Tahoma, Verdana, Helvetica, sans-serif;font-weight:normal;font-size:11px;color:#999999}
    .AFDataText {font-family:Tahoma, Verdana, Helvetica, sans-serif;font-size:11px;font-weight:bold;color:#000000}
    .AFDataTextDisabled {font-family:Tahoma, Verdana, Helvetica, sans-serif;font-size:11px;font-weight:bold;color:#999999}
      
If you want to change the font-family to Arial for everything, then you need to do a replace of every use of Tahoma to Arial. Well, in skinning you don't need to do this. You can use the :alias feature. A skin selector that ends in :alias is one that never gets written out to the generated CSS-2 stylesheet. Instead it is included in other skin selectors and is useful in that you can change css properties in one place, not in tens or maybe hundreds of places. The base skin that you will extend has alias selectors that it includes in other skin selectors. One example is the .AFDarkBackgroundColor:alias. It defines a 'dark' background color, like blue. Then all the component selectors that use a blue background color include (with -tr-rule-ref: selector(".AFDarkBackgroundColor:alias");) this alias instead of 'color: blue'. Then a person skinning their application can change '.AFDarkBackgroundColor:alias' to another color and everywhere it is included will get changed. The alias feature is only useful to skinners if the component developers use the aliases instead of hardcoding things like colors and fonts. Looking at the example above, to change the font-family:Tahoma to Arial you would have to change every style class that uses font-family. If, however, the base skin was defined to use the .AFDefaultFont:alias in these styles, you would only have to skin the .AFDefaultFont:alias in your skin. Here's another example. The form components style their label the same. Let's say they all set the color to blue:
        af|inputText::label,
        af|inputChoice::label,
        af|selectOneChoice::label, etc. {color:blue}
        
An alias key has been created for .AFLabel:alias that is included in all the above keys.
        af|inputText::label,
        af|inputChoice::label,
        af|selectOneChoice::label, etc. {-tr-rule-ref: ".AFLabel:alias"}
        .AFLabel:alias { color: blue }
        
This way if you want all the labels the same, you can change the style properties for the .AFLabel:alias style only.
          In your CSS file you can change the color of all the labels like this:
          
          .AFLabel:alias {color: red}
        

State skinning keys

In CSS there are pseudo-classes, like :hover, :active, :focus. We consider these 'states' of the component. We use this same concept in skinning components. Components can have state, like read-only or disabled. We use the pseudo-class syntax to denote state in skinning selectors. For example, you can style the label when the component is disabled by writing this selector:
        af|inputText:disabled::label {color:gray}
        
        This generates:
        .af_inputText.p_AFDisabled .af_inputText::label {color:gray}
        
        It works on this html:
        
        <span class="af_inputText p_AFDisabled"><span class="af_inputText_label>Label</span></span>
         
        

Skinning properties

Skinning properties are skin css properties that are essentially a key that will be stored in the Skin object with the value. The renderer uses the skin property when it is rendering. The skin properties are documented in skin-selectors documentation. They are available for the skin to use as a way to customize the rendering of a component application-wide, not per-instance. per-instance customizations are component attributes. An example of a skin property is -tr-show-last-item for the navigationPath component. In your skin's css file you can specify whether you want the last item to be shown or not.
          /* For all instances of the navigationPath in the application, do not show the last item */
          af|navigationPath {-tr-show-last-item: false}
          

Icon skinning keys

Some components render icons (<img> tags) within them. For instance the inputDate has a launch icon. Chances are you will want to skin the icons. Even though these icons are not rendered with CSS, like background-image, they are still skinnable in the Skin CSS file. All icon skin keys end with '-icon' or 'Icon:alias'. icons do not get generated to the CSS-2 stylesheet. Instead they get registered with the Skin object and the renderer uses the icon when it is rendering, e.g., Icon obj = skin.getIcon("af|foo::date-icon"); It will use your skinned icon if you have skinned it. Otherwise, it will use the base skin's icon. Note that CSS-syntax like pseudo-classes (:hover, etc) and descendent selectors and composite class selectors do not work with icon selectors. Remember, if you create your own skin selectors, do not end with the name with -icon or 'Icon:alias' if it is for a css style; it will get created as an Icon object.
              af|inputDate::launch-icon {
                content:url(/skins/purple/images/dateButtonPurple.gif);
                width:19px;
                height:24px
              }
              af|inputDate::launch-icon:rtl {
                content:url(/skins/purple/images/dateButtonPurpleRTL.gif);
                width:19px;
                height:24px
              }
              /* You can use a 'text' icon instead of an image icon if bandwidth is a concern, for example */
              af|foo::help-icon {
                content: '?';
              }            
            
Until recently there were limitations with skinning icons that -tr-rule-ref and -tr-inhibit were not supported. Further, the skin properties were not inherited from base skin for icon selectors. We fixed these limitations to make the icon selector behavior consistent with general skinning style selectors. Now that we inherit the skin properties for icons, if there were 'content' and other properties like 'width' and 'height' set, and you override just the 'content' property in your custom skin, you would inherit the 'width' and 'height' from the base skin. While this is a change in behavior, it is a change in right direction. If this behavior is not desirable, skinners can use -tr-inhibit to inhibit some properties or 'tr-inhibit: all' to inhibit all properties before overriding the 'content' property.

Specifying urls

Trinidad's skinning engine supports five URL types: absolute, relative, context relative, server relative and base64 encoded images.
  • Absolute URLs specify the complete URL to the resource, including the protocol (e.g. http://).
  • Relative URLS are used if the specified url does not start with a slash ("/") and if there's no protocol present. A relative URL is based on the skin's CSS file location. For instance, if the skin CSS file is located in MyWebApp/skins/mySkin/ and the specified url is skinImages/myImage.gif, then the final URL will be /MyWebApp/skins/mySkin/skinImages/myImage.gif.
  • Context relative URLS are resolved relative to the context root of the web application. To use them, you simply have to make it start with a single slash ("/"). For instance, if the context root is /MyWebApp and the specified URL is /images/myImage.jpeg, the resulting URL will be /MyWebApp/images/myImage.jpeg.
  • Server relative URLS are resolved relative to the web server as opposed to the context root. This allows you to easily refer to resources located on another application on the same server. To use this type of URL, the specified URL must start with two slashes ("//").
  • base64 encoded image URLS are images embedded directly into the CSS file. This should follow the "data" URL scheme specified in rfc2397 i.e. data:image/[imagetype];base64,[data]
Equivalence Most of the times, you can write equivalent URLs in any of the four forms. For example, if your web application is located at http:www.mycompany.com/MyWebApp/ and your CSS is located in /skins/mySkin/ folder, the following 4 entries are equivalent:
  • Absolute url(http://www.mycompany.com/MyWebApp/skins/mySkin/skin_images/ObjectIconError.gif);
  • Relative url(skin_images/ObjectIconError.gif);
  • Context relative url(/skins/mySkin/skin_images/ObjectIconError.gif);
  • Server relative url(//MyWebApp/skins/mySkin/skin_images/ObjectIconError.gif);
Why are there so many choices for the url? The four ways to specify an URL exist to offer maximum flexibility to our users.

Skinning Text

Text our components render is translatable. The text is abstracted out as resource bundle keys. With skinning you can override resource bundle key values. These key values should be documented in skin-selectors.xml, but if they are not (they are not currently for the Trinidad components), then you'll have to look at the CoreBundle.xrts/.java source file for the keys. The resource bundle key/values are not skinned in the skin's css file. Instead they are skinned in a Map or ResourceBundle and you point to that from your skin definition in trinidad-skins.xml. Let's go through an example. Let's say you want to skin the ShowDetail disclosed tip. Let's say the 'key' is af_showDetail.DISCLOSED_TIP. You would:
  • Create a ResourceBundle file(s) that sets your new value for this key and any other keys you want to change the value for, one file per language.
  • Set bundle-name in the trinidad-skins.xml file for your custom skin.
  • Package your skin and resource bundle classes together in the jar.
    
      public class SkinBundle extends ListResourceBundle
      {
        @Override
        public Object[][] getContents()
        {
          return _CONTENTS;
        }
      
        static private final Object[][] _CONTENTS =
        {
          {"af_tableSelectMany.SELECT_COLUMN_HEADER", "Select A Lot"},
          {"af_tableSelectOne.SELECT_COLUMN_HEADER", "Select Just One"},
          {"af_showDetail.DISCLOSED_TIP", "Click to Hide"}
        };
      }
     <skin>
        <id>
            purple.desktop
        </id>
        <family>
            purple
        </family>
        <render-kit-id>
            org.apache.myfaces.trinidad.desktop
        </render-kit-id>
        <style-sheet-name>
            skins/purple/purpleSkin.css
        </style-sheet-name>
        <bundle-name>
            org.apache.myfaces.trinidaddemo.resource.SkinBundle
        </bundle-name>
    </skin>
  
        The directory structure of your skin jar would look like:
        META-INF
        META-INF/trinidad-skins.xml
        META-INF/org/apache/myfaces/trinidaddemo/resource/SkinBundle.class & SkinBundle_fr.class
        
Then when the renderer renders the text, it will look in your resource bundle first for the key's value. It is highly recommended if you do skin text that you provide all the translated bundles for the key you are skinning. Therefore you will have many other files, like SkinBundle_fr, etc., for each language. Another option for skinning text is to use the translation-source parameter instead of bundle-name. translation-source is an EL binding that points to a Map or a ResourceBundle. The benefit of this option is that you can automatically change the translation value based on any logic that you want at runtime. bundle-name takes precedence if both are set.
  
public class SkinTranslationMapDemo
{
  /* Test a skin's translation-source EL pointing to a Map */
  public Map<String, String> getContents()
  {
    return _CONTENTS;
  }

  static private final Map<String, String> _CONTENTS = new HashMap<String, String>();
  static 
  {
    _CONTENTS.put("af_inputDate.LAUNCH_PICKER_TIP", "Launch PickerMap");
    _CONTENTS.put("af_showDetail.DISCLOSED_TIP", "Hide Tip Map");
    _CONTENTS.put("af_showDetail.DISCLOSED", "Hide Map");

  }
}
      <skin>
        <id>
            purple.desktop
        </id>
        <family>
            purple
        </family>
        <render-kit-id>
            org.apache.myfaces.trinidad.desktop
        </render-kit-id>
        <style-sheet-name>
            skins/purple/purpleSkin.css
        </style-sheet-name>
        <translation-source>#{skinTranslationMap.resourceBundle}</translation-source>
    </skin>

Skinning CSS features

Re-using selectors and properties via -tr-rules

You can do things in a skin css file beyond what you can do in a regular css file. At runtime the skin framework processes the skin's css file and the skin framework pulls out skinning properties and icons and registers them with the Skin object. The skinning framework merges styles together that use the -tr-rule-ref property. The skinning framework picks the styles based on the HTTP request information, like agent and platform and merges them with the non-specific styles. This section will go into more detail on these features and how you can use them in your skinning css file.
  • Skin properties e.g., af|breadcrumbs{-tr-show-last-item:false}. Skin properties for a component are documented in the skin-selectors documentation. Skin properties are useful if you want to control the rendering of a component that css alone cannot do, like displaying the last item or not (well, css might be able to do that). The writer of the renderer may choose to expose skin properties to enable the application developer to have more control over the way the component renders without having to write their own renderer. Another example is af|train {-tr-visible-stop-count: 10}. This allows you to change the number of visible train stops.
  • -tr-inhibit - > e.g., af|foo {-tr-inhibit: padding; color: red} This css property is used to inhibit/reset css properties that you are inheriting from a base skin. "-tr-inhibit: all" will clear out all the inherited properties. "-tr-inhibit: property-name" will inhibit the property name; "-tr-inhibit: padding", for example, will clear out any padding that you have inherited. You need to make sure your property-name matches that in the base skin. If the base skin has padding-right: 6px and you inhibit padding, you will still get the padding-right. You need to inhibit padding-right. Also note that inhibitions happen when the skinning framework processes the skin's css file, and not based on cascading rules in the browser. Therefore the skin selector has to match and the property name has to match exactly to what you are inhibiting. -tr-inhibit works for style selectors, icons (selectors that end with -icon) and skinning properties.
                
    
                Skin A:
                  af|breadCrumbs { -tr-show-last-item: false; }
    
                Skin B extends Skin A:
                  af|breadCrumbs { -tr-inhibit: -tr-show-last-item;}
    
                Skin A does not show the last breadcrumb. Skin B does show the last breadcrumb.
    
                --
                Skin A
                  .SomeStyleClass {
                      color: red;
                      font-size: 11px;
                  }
                Skin B
                  .SomeStyleClass {
                      -tr-inhibit: color;
                  }
    
                Skin A has color: red, but Skin B has no color, since it has been inhibited.
    
                --
                Skin A
                .SomeStyleClass {
                  color: red;
                  font-size: 11px;
                  -tr-inhibit: color;
                }
    
                This definition makes no sense. If your intent is to remove the color attribute,
                then you should simple say:
                .SomeStyleClass {font-size: 11px}
                You shouldn't be setting a property, then inhibiting it in the same definition, or even the same skin.
                Inhibits happen first in the same rule, then the specific styles, so if you inhibit a property, and also
                define a property in the same rule, it won't be inhibited.
                However, if you define .SomeStyleClass {-tr-inhibit: color;} later in Skin A, it will be inhibited.
                Again, do not inhibit within the same skin. It takes more programming cycles to do the merge, and your css
                isn't as clean.
                
              
  • -tr-rule-ref - > This is used to include other styles in your style see :alias section. Application developers probably won't use this. This is meant for component developers to use when they are setting up the base skin for their components. Here are some examples of how you can use -tr-rule-ref and what the generated css would be.
                
                Example 1:
    
                Skin A
    
                    .AFSomeAlias:alias {color: red}
                    .foo {-tr-rule-ref: selector(".AFSomeAlias:alias")}
    
                Skin B extends Skin A
    
                    .AFSomeAlias:alias {color: blue}
    
                Result
    
                If you run Skin A, you will see in your CSS
                    .foo {color: red}
    
                In you run Skin B, you will see in your CSS
                    .foo {color: blue}
                because the alias that .foo includes (.AFSomeAlias:alias) in Skin A has
                overwritten its color property in Skin B to blue.
    
                ---
                Example 2:
    
                Skin A
    
                    .myClass {color: orange}
    
                Skin B extends Skin A
    
                    .MyBlueColor:alias {color: blue}
                    .myClass {color: pink; -tr-rule-ref: selector(".MyBlueColor:alias")}
    
                If you run Skin A, you will see in your CSS
                    .myClass {color: orange}
    
                In you run Skin B, you will see in your CSS
                    .myClass {color: pink}
    
                In Skin B's definition for .myClass, all 'includes' (like -tr-rule-ref) get merged in first,
                then specific property definitions (like color in this case) get merged in last for the rule.
                Therefore, color: pink overwrites -tr-rule-ref: selector(".MyBlueColor:alias");
                However, if you define .myClass {-tr-rule-ref: selector(".MyBlueColor:alias")} later in Skin B, it will be blue.
    
                If you are unsure of what includes or inhibits resolve to, you can run your own skinning test like
                above, and look at the generated css file.
                
              
  • -tr-property-ref - > This is used to include properties from one style into another. The syntax of -tr-property-ref is: background-color: -tr-property-ref("af|foo","color"), where the first parameter is the style from which the property is included and the second one is the name of the included property in that style. There can be more than one occurrence of -tr-property-ref in a property value, for example: background: -webkit-linear-gradient(top, -tr-property-ref("af|foo","color") 0%, -tr-property-ref("af|bar","color") 100%); When the property included has the same name as in the including selector, the following syntax can also be used: background-color: -tr-property-ref("af|foo") instead of: background-color: -tr-property-ref("af|foo", "background-color"). This is useful when only some properties from another style are needed to be included. In this case, we won't need to include the entire style via a -tr-rule-ref rule, but only the needed property.
  • +/- - > This feature of the skinning framework allows you to set a selector's color or font that is relative to another selector's color or font. This is useful if you have color ramps. You can change the color in one place. An example for the color is:
              
              .BaseBG:alias { background-color: #0099ff; }
              .fooColorTest {
                 -tr-rule-ref: selector(".BaseBG:alias");
                 background-color: +#333333;}
              .fooColorTestMinus {
                -tr-rule-ref: selector(".BaseBG:alias");
                background-color: -#333333;
              }
    
        this resolves to
    
              .fooColorTest {background-color:#33ccff}
              .fooColorTestMinus {background-color:#0066cc}
              
              
    The same thing works for fonts.
                
                  .FontSizeTest:alias {font-size: 12pt;}
    
                  .fooFontTest {
                    -tr-rule-ref: selector(".FontSizeTest:alias");
                    font-size:+1pt;
                  }
                
              

Server side at-rules

You might not want your selector's css properties to be applied to all browsers, all platforms, all locales, and both reading-directions. For example, you might have to tweak some padding in IE that you don't need on any other browser. You might want the font-style to be different on Windows than it is on other platforms. To style a selector for a particular user environment, you put that skinning information inside a skinning framework @rule or ':rtl' pseudo-class. The skinning framework picks the styles based on the HTTP request information, like agent and platform and merges them with the styles without rules. These css properties that match the 'rules' get merged with those outside of any 'rules'; the most specific rules that match a user's environment take precedence. These rules are resolved at server side and matching selectors are rendered for the user agent. (See an example in the css code below.) The skinning framework currently supports these server side 'rules':
  • @import Use @import to import style rules from other skinning css files. The URL of the file you are importing is relative to the skin file you are importing into. The following syntax is supported: @import "purpleBigFont.css"; @import url("purpleBigFont.css"); @import url(purpleBigFont.css);
  • @platform {/skin definitions go here/} - > Possible values are:
    • windows
    • macos
    • linux
    • solaris
    • ppc
    • iphone
    • blackberry
    • nokia_s60
    • genericpda
    • android
    • unix
    This is to define styles only for a particular platform.
  • @agent {/skin definitions go here/} - > Possible values are:
    • netscape (for Netscape Navigator browser)
    • ie (for the Microsoft Internet Explorer browser)
    • gecko or mozilla (for browsers based on the Gecko Layout Engine, eg: Mozilla, Netscape 7.0+)
    • elaine (Palm Web clipping)
    • ice or icebrowser (for the ICE Browser)
    • pixo (for the Pixo Microbrowser)
    • wml (for the WML Microbrowser)
    • simple-result (for SimpleResult intermediate Form)
    • ptg (for iAS wireless (PTG) client)
    • netfront (for the NetFront browser)
    • webkit (for the Safari browser)
    • blackberry (for the BlackBerry browser)
    • nokia_s60 (for the Nokia S60 browser)
    • genericpda (for the basic HTMLbrowser)
    • konqueror (for Konqueror)
    • email (for email)
    • opera (for opera browser)
    • googlebot (for Google web crawler)
    • msnbot (for Bing web crawler)
    • oracle_ses (for Oracle SES)
    • genericDesktop (for generic desktop)
    This is to define styles only for a particular agent.
            @agent ie
            
    Matches any version of Internet Explorer Agent type may be optionally followed by "and" separated version rules:
            @agent ie and (version: 6)
            
    Matches any 6.x version of Internet Explorer Rules may be OR'ed together using comma-separated rules:
            @agent ie and (version: 6), ie and (version: 7), gecko and (version: 1.9)
            
    Matches either Internet Explorer version 6.x or Internet Explorer version 7.x or Gecko engine version 1.9 (in FireFox for example). These are the same:
    	@agent ie and (version: 6.*)
    	@agent ie and (version: 6)
    	
    So to match only 6.0.x use:
    	@agent ie and (version: 6.0)
    	
    Similarly, you can use max-version and min-version to specify ranges of versions. The rule:
            @agent ie and (version: 6), ie and (version: 7), gecko and (version: 1.9)
            
    can be written as:
            @agent ie and (min-version: 6) and (max-version: 7), gecko and (version: 1.9)
            
    You can use touchScreen to specify styles/selectors for touch screen devices. Agent captures a capability named "touchScreen" which is set to none, single or multiple.
            @agent (touchScreen:none)
            @agent (touchScreen:single)
            @agent (touchScreen:multiple)
            
    Matches the corresponding touchScreen agent capability. A short cut provided to specify any touchScreen device is:
            @agent (touchScreen)
            
    Matches all agents with touchScreen capability set to single or multiple. You may notice that we have not specified agent name for the touchScreen examples till now. Agent name is made optional for touchScreen rules.
            @agent webkit and (touchScreen)
            
    Matches webkit run on all touch devices (single or multiple touch). touchScreen can be specified with multiple ANDs and ORs as well.
            @agent webkit and (version: 9) and (touchScreen:single)
            
    Matches single touch device running version 9 of webkit.
            @agent ie and (version: 9), webkit and (touchScreen:multiple)
            
    Matches either version 9 of Internet Explorer or agent with multiple touch capability.
  • @accessibility-profile {/skin definitions go here/} - > Possible values are: high-contrast, large-fonts. This is to define styles only for a particular accessibility profile. See the Configuring Apache Trinidad chapter for information on setting the accessibility profile.
  • @locale {/skin definitions go here/} - > A certain locale must be specified, either only the language or both the language and the country. This is to define styles only for a particular language and country.
  • @mode {/skin definitions go here/} - > Possible values are quirks or standards. This is to define styles particular only for browser quirks or standard mode.
/** for ie and gecko on windows, linux and solaris, make the color pink **/
@platform windows, linux, solaris
{
  @agent ie, gecko
  {
    af|inputText::content {background-color:pink}
  }
}

af|someComponent {color: red; width: 10px; padding: 4px}

/* for ie, we need to increase the width, so we override the width.
We still want the color and padding; this gets merged in. We want to add height in IE.  */
@agent ie
{
  af|someComponent {width: 25px; height: 10px}
}

/* for ie 5 and 6, we also need some margins.*/
@agent ie and (version: 5), ie and (version: 6)
{
  af|someComponent {margin: 5px;}
}

/* for Firefox 3 (gecko 1.9) use a smaller margin.*/
@agent gecko and (version: 1.9)
{
  af|someComponent {margin: 4px;}
}

@locale en, de {
   af|commandButton {
      color: red;
   }
 }
 @locale ro-RO {
   af|commandButton {
     color: blue;
   }
 }

/* The following selectors are for all platforms and all browsers */

/* don't use the base skin's background-image */
af|navigationPane::tabs-active af|navigationPane::tabs-bottom-start-content {
  -tr-inhibit: background-image;
}

/* this should end up with  .portlet-form-input-field {padding: 8px} */
.portlet-form-input-field {
/* This should first inhibit all inherited styles. Then everything else
   should be included.*/
  -tr-inhibit: all;
  padding: 8px;
  /* This should inhibit the background-color that is inherited and/or included,
  like in .AFLightAccentBackground:alias
  The order of this does not matter. */
  -tr-inhibit: background-color;
  -tr-rule-ref: selector(".AFLightAccentBackground:alias");
}
        
        

Client side at-rules

If you want to use rules that are supported out of the box by CSS like @document, @media etc, you can specify those rules in the skin file and expect them to be rendered directly into the browser. These rules will be evaluated by the browser and styles will get applied if the browser passes the conditions mentioned in the rules. The skinning framework currently supports these client side 'rules':
  • @document
  • @font-face
  • @keyframes
  • @media
  • @page
  • @supports
If you use any rule which is not listed in either server side at-rules or client side at-rules, it will be rendered as client side rule similar to @media or @document.

Notes:

  • Styles inside client side at-rules cannot be used with tr-rules. This is because, in server side, we do not know if the referred style will be rendered. On the contrary, tr-rules can be used inside client side at-rules to refer styles or properties define outside client rules.
  • Aliases cannot be specified inside client rules. Aliases are evaluated at the server side, and thus we cannot pick aliases based on client rules.
Some example usages:

af|document {
  background-color: white;
  background-image: url('/skins/custom-base/top-band.png'), url('/skins/custom-base/texture.png');
  background-repeat: repeat-x, repeat;
  background-position: top left, top left;
  background-size: 10px 61px, 6px 6px;
}

/* specify different background-image for min-device-pixel-ratio = 2 */
@media only screen and (min-device-pixel-ratio: 2) {
  af|document {
    background-image: url('/skins/custom-base/top-band@2x.png'), url('/skins/custom-base/texture@2x.png');
  }
}

/* specify different background-image for min-resolution: 2dppx*/
@media only screen and (min-resolution: 2dppx) {
  af|document {
    background-image: url('/skins/custom-base/top-band@2x.png'), url('/skins/custom-base/texture@2x.png');
  }
}

.Some:alias {
  color: red;
  background-color: green;
}

@media only screen {
  af|mystyle {
    -tr-rule-ref: selector(".Some:alias");
  }
  af|mystyle2 {
    background-color: -tr-property-ref(".Some:alias","color");
  }
}
          

Client side at-rules can be nested in server side at-rules and vice versa. One slight deviation is for @page and @font-face rules. These can be wrapped in a server side at-rule, but cannot contain a inner server side at-rule because these rules directly contain CSS properties unlike other client side at-rules which contains complete styles.

@agent gecko {
  @page:first {
    margin:2in;
  }
}

@keyframes mymove {
  @agent gecko {
    0% { top: 0; left: 0; }
    30% { top: 50px; }
    68%, 72% { left: 50px; }
    100% { top: 100px; left: 100%; }
  }

  @agent ie {
    0% { top: 1; left: 1; }
    30% { top: 100px; }
    100% { top: 200px; left: 100%; }
  }
}
          

Server side pseudo classes

Generally, pseudo classes are executed at the client side. CSS supports pseudo classes such as ":hover", ":focus" etc and these pseudo classes are rendered as is into the CSS generated by skinning framework and gets applied by the browser automatically. There are some special pseudo classes supported by skinning framework, which are evaluated at the server side. These pseudo classes are not rendered into the generated CSS, but the decision to render the selector is taken at the server side base on the user agent or application configuration.
  • :ltr - > pseudo-class to create a style or icon definition when the browser is in a left-to-right language.
  • :rtl - > pseudo-class to create a style or icon definition when the browser is in a right-to-left language. The best example is that of images that are not symmetric. If you set a skin selector that uses a asymmetrical image, when you set your browser to the right-to-left reading direction, then you want your image to be flipped. To do this, you use the :rtl pseudo-class at the end of your selector and point it to a flipped-looking image.
    /* rounded corners on the top-start and top-end */
    /* shows how to use :rtl mode pseudo-class. The start image in ltr mode is the same as the
    end image in the right-to-left mode */
    af|panelBox::medium af|panelBox::top-start,
    af|panelBox::medium af|panelBox::top-end:rtl {
      background-image: url(/skins/purple/images/panelBoxStart.gif);
      width:8px;
      height:8px
    }
    
    af|panelBox::medium af|panelBox::top-end,
    af|panelBox::medium af|panelBox::top-start:rtl {
      background-image: url(/skins/purple/images/panelBoxEnd.gif);
      height: 8px;
      width:  8px;
    }
                  
  • :lang - > Not yet implemented in Trinidad.
Note about "rtl" and "lrt" pseudo classes: A selector which does not use "lrt" or "rtl" pseudo class is applicable to both. A selector with "lrt" or "rtl" pseudo class will override one without it. "lrt" or "rtl" pseudo class selectors are used to define selectors specific to the direction.

Package your Skin in a JAR file

If you want another application to be able to use your skin, you can package it up into a JAR file, and include the JAR on the application's classpath. The Skinning Framework finds all META-INF/trinidad-skins.xml files on the classpath. To package up your skin into a JAR file, you need this directory structure:
  • META-INF/trinidad-skins.xml
  • META-INF/skins/yourSkin.css
  • META-INF/adf/yourImages
The trinidad-skins.xml file needs to be in META-INF for the Skinning Framework to find it. The .css file needs to be somewhere within the META-INF directory; it doesn't have to be in a /skins directory. The images need to be in a META-INF/adf directory in order to kick in the resource servlet that will in turn serve up the images to the browser. Here's an example:
  • META-INF/trinidad-skins.xml
  • META-INF/skins/purpleSkin.css
  • META-INF/adf/images/*.png
In trinidad-skins.xml, you reference the css file like this:
      
      <style-sheet-name>
        skins/purpleSkin.css
      </style-sheet-name> 
      
      
In purpleSkin.css, you reference your images relative to the skin css file like this:
      background-image: url(../adf/images/progressbar_empty.png)
      
or you can make it relative to the servlet context like this:
      background-image: url(/adf/images/progressbar_empty.png)
      
In the generated css file, you should see the /adf immediately after the context root for the servlet resource to kick in.
      background-image:url(/trinidad-demo-context-root/adf/images/progressbar_empty.png);
      
In Trinidad the web.xml file has a servlet mapping to the resources servlet to serve up the images from the css file to the browser. The url-pattern is /adf/*, which is why you need to have your images in a META-INF/adf directory. Alternatively, you can put your images in a directory that does not have 'adf', but you will need to write your own ResourceServlet and add the mapping to web.xml.
	
	<servlet-mapping>
	<servlet-name>resources</servlet-name>
	<url-pattern>/adf/*</url-pattern>
	</servlet-mapping>
	
	

Tips and Tricks

View uncompressed styleclass names

When you run a page and view the html source, you will see styleclasses like class="x10". To see instead something more meaningful like class="af_inputText_content" you need to disable the styleclass compression which is enabled by default for performance reasons. To disabled the compression, you need to add this to the web.xml file:
                  
        <context-param>
         <param-name>org.apache.myfaces.trinidad.DISABLE_CONTENT_COMPRESSION</param-name>
         <param-value>true</param-value>
        </context-param>
                    
        
Note: Trinidad releases prior to 1.0.3/1.2.4 used an 'internal' key, org.apache.myfaces.trinidadimpl.DISABLE_CONTENT_COMPRESSION

See your skin changes with refresh of browser

When you change your skin css file, you will need to stop/restart the server to see your changes unless you set the check file modification flag in your web.xml file. If that flag is set, then you can refresh your browser to see your css changes, though currently you will not see your server-side changes which are 'icon' and 'server-side skin properties'. Remember to set this back to false when you are done for performance sake.
                  
        <context-param>
         <param-name>org.apache.myfaces.trinidad.CHECK_FILE_MODIFICATION</param-name>
         <param-value>true</param-value>
        </context-param>
                    
        

Firebug

Install Firebug on Firefox. With this tool you'll be able to view the rendered components and the css properties of the various dom elements. This will help you figure out what skin selectors are causing the css styling. Alternately, you can view the rendered CSS file itself. To map a rendered styleclass to a skin selector you'll need to look at the skin-selectors.html document.
          A skin selector like :
          
          "af|panelBox" gets generated as ".af_panelBox"
          
          "af|inputText:read-only::content" gets generated as ".af_inputText.p_AFReadOnly .af_inputText_content".
          
          Any non-css-2 pseudo-classes gets generated as .p_AF*, otherwise they pass through.
          
          "af|commandLink:hover" gets generated as ".af_commandLink:hover"
          

Simple (default) skin

To see what you get out of the box without any skinning on your part, you should run the simple skin, which is the default. You can use Firebug, or view the generated css file. Or, if you look have the source code, you can look at base-desktop.xss or simple-desktop.xss. These files are not public and are in in an xml-based format, not CSS, so it might be harder to understand. If you get some property from the base skin that you don't want, you can use the -tr-inhibit property: af|foo {-tr-inhibit: all} to inhibit all the base skins' properties and start fresh.

or

af|foo {-tr-inhibit: padding} to inhibit the padding css property.

The property name has to match the base skin's property exactly, so looking at the base-deskop.xss will help.

W3C's CSS specification

You need to be very familiar with CSS to do complex skinning. Some useful concepts to know are the different types of selectors, like descendent selectors, composite class selectors, and selector specificity. You can look at the w3c's css spec for detailed information. Selector specificity is really important to understand, because you might get into a situation where you are not sure why your style is not getting picked up. It could very well be that the base skin has a selector that is more specific than your selector and thus the browser gives it more weight regardless of the position in the generated css file.

Using the styleClass attribute

You can skin a particular instance of a component by combining the skin selectors and the component's styleClass attribute value. For example, you can put a styleclass on the commandLink component and then skin it
          <tr:commandLink styleClass="myCommandLink">
          
          /* style any commandLink components that have their styleClass attribute set to 'myCommandLink' */
          af|commandLink.myCommandLink {color: red}
          
Note that this does not work in Internet Explorer browsers earlier than IE7. You'll have to do this for older browsers: .myCommandLink {color: red}

Performance when many different skins in an application

The skinning framework caches information for the generated css file for each skin that is requested. This could be a source of a memory leak if your application is built to use many different skins, like each user has his own custom skin. Fortunately, this is not a common usecase. In this case, you would want to limit the number of generated css files we cache in our LRUCache (least-recently used cache). You can do this by specifying the maximum number of skins you want to cache with the web.xml context-param org.apache.myfaces.trinidad.skin.MAX_SKINS_CACHED

Skinning for Custom Component Developers

This section is for a custom component developer. This section is not for someone who is skinning their application. If you created your own custom component, and you want this component to work well with other existing skins, then you need to create a skin-addition. A skin-addition gives you a way to 'push' your own stylesheet and resource bundle for your components into existing skins, most likely the simple skin. Then you can jar up your component and skin information and your component can be used by others. (Another way to do the same thing is to open up the simple-desktop.xss file and other skin css files, and add your skin definitions there. This is not practical if you don't own this source.) Skin objects contain zero or more SkinAdditions. The SkinAdditions' stylesheets are merged into the Skin's own stylesheet. The SkinAdditions' resource bundle is looked at along with the Skin's own resource bundle when Skin's getTranslatedValue is called by your renderer.

Creating a skin addition declaratively

Create a skin stylesheet file (say, myComponents-simple-desktop.css) that defines your icons, styles, and properties so that your components will fit in with the simple.desktop skin (or whatever skin you want to fit in with). Use the Skin Selector Aliases as much as possible. This way if a person wants to change the default font for the entire application, he can create a skin that changes the .AFDefaultFont:alias and your component's font will change, too. If you didn't include the alias in your skin selector properties, then your font won't change. (See Skin-Selectors.html documentation) Register this StyleSheet file with the simple.desktop skin. Create a META-INF/trinidad-skins.xml file if you don't already have one.
          
          <?xml version="1.0" encoding="ISO-8859-1"?>

            <skins xmlns="http://myfaces.apache.org/trinidad/skin">
                <skin-addition>
                    <skin-id>
                        simple.desktop
                    </skin-id>
                    <style-sheet-name>
                        styles/myComponents-simple-desktop.css
                    </style-sheet-name>
                    <bundle-name>
                        org.mycompany.view.resource.myComponentsResourceBundle.java
                    </bundle-name>
                </skin-addition>
            ... more skins or skin-additions could go here ...
            </skins>
            
         
  • skin-id - The id of the skin you are 'pushing' your styles into.
  • style-sheet-name - This is your skin's stylesheet name url. This should be a uniquely name path, since we use ClassLoader getResource to find this file, and if another jar has the same style-sheet-name, it might find that file and not yours.
  • bundle-name - This is the package where the skin's resource bundle lives. This is important for a custom component developer so that the component's text supports different languages.
If you want your component to be skinnable, then you should document the selectors similar to the skin-selectors.html document we have in Trinidad.

Serving up your stylesheet's image urls

Feel free to jar this file up with your other files, because we can find trinidad-skins.xml files in the META-INF directory. However, we can't automatically find any image urls; so if you have image urls, like background-image: url("../arrow.png"); in your myComponents-simple-desktop.css file, you'll have to either put the images relative to the servlet context (start with a /), or create a ResourceLoader and make the path relative to the css file, like "../../xyz/images/drill.gif". These are the steps to create your own ResourceLoader:
  • Add an extra servlet-mapping to web.xml for your resource path, like "/xyz/*" as opposed to "/adf/*".
  • Create a ResourceLoader file, like org.mycompany.resource.XYZResourceLoader
            package oracle.adfinternal.view.faces.xyz.resource;
    
    import org.apache.myfaces.trinidad.resource.ClassLoaderResourceLoader;
    import org.apache.myfaces.trinidad.resource.RegexResourceLoader;
    import org.apache.myfaces.trinidad.resource.ResourceLoader;
    public class XYZResourceLoader extends RegexResourceLoader
    {
     public XYZResourceLoader(ResourceLoader parent)
     {
         register("(/.*\\.(css\|jpg\|gif\|png\|jpeg\|js\|svg))",
         new ClassLoaderResourceLoader("META-INF", parent));
     }
    }
            
  • Create a .resources file and place it in META-INF/servlet/resources. In the file, place a single line that references the resource loader class. For example, create an xyz.resources file with the following single line: org.mycompany.resource.XYZResourceLoader
  • Place the .resources file in your JAR, so its path is META-INF/servlet/resources/xyz.resources.
  • Add resources in your jar at the following location: META-INF/xyz/*, which needs to match the servlet-mapping
  • Create a URL reference to the image with "xyz" in the path.
  • all files created above should be in the same JAR that contains your custom components/faces-config.xml/etc.

Pre-generating Skin Style Sheets

Trinidad skin definitions are dynamically converted into browser-consumable style sheets (.css files) at runtime. While a runtime-based approach is suitable for most application deployments, in some cases it may be desirable to offload style sheet delivery to some other server, eg. to a server that is managed by a CDN provider, or even just to a static http server that can be shared across multiple applications. Trinidad provides a skin pre-generation "service" to address such cases.

The skin pre-generation service can be accessed from any Trinidad-based application. To enable this service, simply specify the following system property when starting up the application server:

-Dorg.apache.myfaces.trinidad.SKIN_PREGENERATION_SERVICE=on

Enabling the skin pre-generation service causes a special "/-tr-pregenerate-skins" view id to be exposed. Requests into this view trigger pre-generation, which produces a set of static style sheet files that can then be copied/uploaded to some other server.

The /-tr-pregenerate-skins view id requires one mandatory request parameter: id. The value of this parameter must be an id of one of the skins registered with Trinidad.

For example, in an application that uses prefix mapping for the FacesServlet, enabling the skin pre-generation service exposes the following uri:

/context root/faces/-tr-pregenerate-skins?id=minimal.desktop

Hitting this uri triggers pre-generation of style sheets for the minimal.desktop skin.

Skin pre-generation can be slow and can produce a significant amount of content, particularly for large skins with many variants. To compensate for this, only style sheets for the most common variant values are generated by default. In particular, style sheets corresponding to the following variants are generated:

  • plaform: android | iphone | linux | macos | windows
  • agent: ie | gecko | webkit
  • locale: default (unmatched) locale
  • reading direction: ltr
  • accessibility: standard contrast, medium fonts

The "variants" request parameter can be used to force generation of all possible style sheets. For example, specifying variants=all:

/context root/faces/-tr-pregenerate-skins?id=minimal.desktop&variants=all

Will result in in pre-generation of every possible style sheet for the minimal.desktop skin.

In addition to the "variants" request parameter, the following optional request parameters may be used to provide information about how pre-generation should be performed:

  • containerType: servlet | portlet. (Defaults to "servlet".)
  • requestType: nonsecure | secure. (Defaults to "nonsecure".)
  • styleClassType: compressed | uncompressed. (Defaults to "compressed".)

All of the contextual request parameters support multiple values. For example, the following request:

/context-root/-tr-pregenerate-skins?id=minimal.desktop&styleClassType=compressed&styleClassType=uncompressed

Pre-generates style sheets for both compressed and uncompressed style class names for the common variants of the the minimal.desktop skin.

By default, pre-generated style sheets are written into the web application's style sheet cache directory (typically, <web app temp dir>/adf/styles/cache). However, an alternate target directory for the pre-generated output can be specified via the following system property:

-Dorg.apache.myfaces.trinidad.SKIN_PREGENERATION_SERVICE_TARGET_DIRECTORY=directory path

Requests that result in successful skin pre-generation produce status 200 responses. Failed requests (eg. for invalid skin ids) result in non-200 status codes.

For security purposes, pre-generation must never be enabled in end user facing, production deployments - ie. skin pregeneration is CPU and I/O intensive, and must not be exposed to arbitrary users. To avoid the potential for abuse, enabling skin pregeneration has the side effect of disabling the rest of the application. As such, application servers that are enabled for skin pregeneration can only be used for this single purpose.

Style Sheet File Names

Some applications may need to write regular expressions that target specific style sheet file names, eg. for selective uri rewriting purposes. For such cases, applications may depend on the file name structure described below.

Generated style sheet file names are broken up into three major sections:

  1. Skin identifier section
  2. Variant identifier section (eg. agent, locale, etc...)
  3. Contextual identifier section (eg. portlet vs. servlet, etc...)

These major sections are separated by double dashes ("--").

Within each major section, minor subsections appear in a predictable order, separated by single dashs ("-").

The skin identifier section (section #1) contains the following subsections:

  <id>-<version hash>

Where:

  • id: the skin id. Note that the skin id itself may include dash characters (eg. "simple-desktop").
  • version hash: a hash of the skin contents, which typically varies from version to version.

The variant identifiers section (section #2) contains the following subsections:

  <platform>-<agent>-<version>-<locale>-<direction>-<accessibility>

Where:

  • platform: the platform name (eg. linux, windows, iphone)
  • agent: the agent name (eg. ie, gecko, safari)
  • version: the agent version (eg. 7, 1.9.2, 534)
  • locale: the locale (eg. en, en_US, ja).
  • direction: the reading direction (ltr|rtl)
  • accessibility: accessibility profile preferences for high contrast and large fonts (hc|lf).

In the event that no @-rule variant is expicitly matched, only default styles (ie. styles that apply to all requests) are included in the generated style sheet. The token "d" is used to identify this case. For example, if the skin does not define any @locale rules, the locale portion of the file name will be the token "d".

In some cases it is not possible to determine a unique value for a particular variant, because only @-rules that specify multiple values are matched. In this case, a combination of the matched values will be used for the segment. For example, if the skin defines a single @locale rule matching the ja, zh, and ko locales, the locale portion of the file name for generated style sheets corresponding to these locales will be "ja_ko_zh".

The contextual identifiers section (section #3) contains the following subsections:

  • container type: identifies whether the application is hosted within a servlet or portlet (s|p).
  • request type: identifies whether the style sheet is used for a secure or non-secure request (s|n).
  • style class type: identifies whether the style sheet is generated with compressed or uncompressed style classes (c|u).

The following sample style sheet name:

sample-desktop-jh4phm--linux-gecko-1.9.0-ja_ko_zh-ltr-lf--s-n-c.css

Is composed of these pieces:

  • Skin identifier section
    • id: sample-desktop
    • version hash: jh4phm
  • Variant identifier section
    • platform: linux
    • agent: gecko
    • version: 1.9.0
    • locale: ja_ko_zh (style sheet is used for Japanese, Korean and Chinese languages)
    • direction: ltr (left-to-right)
    • accessibility: lf (large fonts)
  • Contextual identifier section
    • container type: s (servlet)
    • request type: n (non-secure)
    • style class type: c (compressed style classes)

When writing regular expressions that target this structure, the following recommendations should be observed:

  • The identifier section contains a variable number of subsection separators: skin ids may contain dashes. As such, when targeting regular expressions at items in the subsequent major sections, it is best to anchor these regular expressions to the major section separators (double-dash).
  • In the future it is possible that new subsections may need to be added to the variant or contextual identifier sections. If the need for additional information arises, new subsections will be added at the end of the appropriate major section. As such, regular expressions should be written in a way that they can tolerate new subsections later appearing at the end of each major section.

Advanced Skinning Features

Skin Provider

SkinProvider feature can be used to create external skin repositories. These repositories can create, manage and de-support Skins at runtime. This is achieved using "org.apache.myfaces.trinidad.skin.SkinProvider" SPI. Classes implementing this abstraction should be listed in the jar's /META-INF/services folder inside of a text file named "org.apache.myfaces.trinidad.skin.SkinProvider". These services will then be run by Trinidad skinning framework and be queried when a Skin is requested. The SkinProvider should publish a list of supported skins by means to the getSkinMetadata API and provide skin matching the requested metadata using the getSkin API. An instance of the SkinProvider can be obtained from the static factory method in SkinProvider and can be used to query skins. SkinProvider authors can make use of this to get hold of the base skins from which they want to extend their skins from. SkinProvider authors can also make use of SkinExtensionFactory as a easy way to to create SkinExtensions instead of implementing the abstract Skin APIs. It is the SkinProvider's responsibility to manage the skins. SkinProviders can decide to return a cached skin or a new skin object for subsequent request for the same Skin. There is no binding on the SkinProvider to support a skin during its lifetime. SkinProvider can decide to de-support a skin any time by not responding to a particular skin request which it served earlier and also de-listing the skin from the getSkinMetadata API. SkinProvider can also make use of Skin.setDirty() method to mark a skin as dirty if there is a modification in the contents of the skin and it needs the skin framework to regenerate the css for the skin. Skin framework will regenerate the css if the skin or any of its parent skins are dirty. SkinProvider tried to find the best match for the requested skin. If more than one provider supplies a matching skin, then the winner is decided based on the best version match, default skin or leaf skin in the family. If there is an un-resolvable conflict then skin provider will choose to return the first skin which it found.

Frequently Asked Questions

There is a Trinidad Wiki where we post FAQs. http://wiki.apache.org/myfaces/Trinidad_Skinning_FAQ