Use css to style a custom spark skin in Flex 4
I’ve finally had a chance to dive in to Flex 4. One trick I learned is incredibly powerful. CSS is a powerful way to specify settings to something from the outside, but it quickly becomes a pain if you need to push pixels around. I’m going to show you how you can create a skin that reads its properties from a stylesheet. This allows css to do what it does best: set simple properties, and lets you do the pixel pushing in the Skin.
Let’s start with some Flex 3 code we’d like to duplicate. This will create a “modal” overlay that covers the app with a translucent screen. You might use this for a LightBox.
<!-- MyOverlay.mxml -->
<mx:Canvas
width="100%" height="100%"
styleName="overlay"
>
<!-- Put a panel or something here -->
</mx:Canvas>
/** styles.css **/
.overlay {
background-color: #000000;
background-alpha: 0.7;
}
Our First Skin
We want to create this in Flex 4. The (rough) equivalent to canvas is a Group, but it isn’t skinnable, so we can use a SkinnableContainer instead.
<!-- MyOverlay.mxml -->
<s:SkinnableContainer
width="100%" height="100%"
skinName="overlay"
>
<!-- Stuff -->
</s:SkinnableContainer>
SkinnableContainer doesn’t have a way to set backgroundColor or backgroundAlpha! The css will not work.
Let’s create the same functionality in a skin instead.
<!-- skins/OverlaySkin.mxml -->
<s:SparkSkin ... >
<!-- The highest-level base class that can use this-->
<fx:Metadata>
[HostComponent("spark.components.SkinnableContainer")]
</fx:Metadata>
<!-- specify all the states the host component uses -->
<s:states>
<s:State name="normal"/>
<s:State name="disabled"/>
</s:states>
<!-- Here's our overlay! -->
<s:Rect left="0" right="0" top="0" bottom="0">
<s:fill>
<s:SolidColor color="#000000" alpha="0.7"/>
</s:fill>
</s:Rect>
<!-- Put the panel, or whatever is in the component here -->
<s:Group id="contentGroup" verticalCenter="0" horizontalCenter="0">
<s:layout><s:BasicLayout/></s:layout>
</s:Group>
</s:SparkSkin>
That’s not too bad. We have a skin, and now we can apply it to our component by setting the skinClass property. For example:
<!-- MyOverlay.mxml -->
<s:SkinnableContainer
width="100%" height="100%"
skinClass="skins.OverlaySkin"
>
<!-- content -->
</s:SkinnableContainer>
Or we can set the skinClass in the style.
<!-- MyOverlay.mxml -->
<s:SkinnableContainer
width="100%" height="100%"
styleName="overlay"
>
<!-- content -->
</s:SkinnableContainer>
/** styles.css **/
.overlay {
skinClass: ClassReference("skins.OverlaySkin");
}
Using CSS on the Skin
Now we have a styleName we can apply to any SkinnableContainer and it will make it look like our black, translucent overlay. This works great, so we start using it, but pretty soon we realize that we need to be able to add a background to another container, but we don’t want to create a new skin just to tweak the color and alpha. Here is where it gets fun. We change the Rect tag in our skin to look like this:
<s:Rect left="0" right="0" top="0" bottom="0">
<s:fill>
<s:SolidColor color="{getStyle('backgroundColor')}" alpha="{getStyle('backgroundAlpha')}"/>
</s:fill>
</s:Rect>
Now it will get it’s color and alpha from the style. We can change our overlay style to look like this
/** styles.css **/
.overlay {
background-color: #000000;
background-alpha: 0.7;
skinClass: ClassReference("skins.OverlaySkin");
}
That looks a lot more like the Flex 3 version! We can then add more styles for other kinds of containers that need backgrounds, and our skin is now much more useful than before.
The Final Version
<!-- skins/BackgroundSkin.mxml -->
<?xml version="1.0" encoding="utf-8"?>
<s:SparkSkin xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/halo">
<fx:Metadata>
[HostComponent("spark.components.SkinnableContainer")]
</fx:Metadata>
<s:states>
<s:State name="normal"/>
<s:State name="disabled"/>
</s:states>
<s:Rect left="0" right="0" top="0" bottom="0">
<s:fill>
<s:SolidColor color="{getStyle('backgroundColor')}" alpha="{getStyle('backgroundAlpha')}"/>
</s:fill>
</s:Rect>
<!-- Tells the skin where to put the children you have specified -->
<s:Group id="contentGroup" verticalCenter="0" horizontalCenter="0">
<s:layout><s:BasicLayout/></s:layout>
</s:Group>
</s:SparkSkin>
/** styles.css **/
.overlay {
background-color: #000000;
background-alpha: 0.7;
skinClass: ClassReference("skins.OverlaySkin");
}
.error {
background-color: #FF0000;
background-alpha: 1.0;
}
.success {
background-color: #00FF00;
background-alpha: 1.0;
}
Using this technique will allow you to play to the strengths of both CSS and Spark Skins.






Try this instead: color="{getStyle('backgroundColor') || 0x000000}" which should use black if the style is null.
I haven't tried it, but it might work.
One question though, can I define the layout type in host component instead of the skin file? I want to use the background rect in many containers but the layout type differs... any suggestions?
Can we define the style names in the component to class selector, ie:
DataGrid
{
backgroundAlpha: 0.2;
headerStyleName: "GridHeader";
}
.GridHeader
{
color: #594733;
}
So for SkinnableContainer, something like:
SkinnableContainer
{
styleName:rectStyle;
}
.rectStyle
{
background-color:Black;
}
color="{getStyle('backgroundColor') || 0x000000}"
I tried this with backgroundAlpha
color="{getStyle('backgroundAlpha') || 0.5}"
But I was wishing to set it to 0 in my style:
<customContainer backgroundAlpha="0" />
but 0 == false, so the OR fails and the backgroundAlpha is set to 0.5
Workaround is to use a ternary operator in the skin, testing explicitly for undefined instead of anything that's equal to false (0, undefined, null, false, etc):
<s:Group alpha="{getStyle('backgroundAlpha') != undefined ? getStyle('backgroundAlpha') : 0.2}" />
:)
However, there is a bug (maybe). By doing so, if you have a transition for a state that to change a property of hostComponent, it won't work. Such as: Resize heightTo="5" target="{hostComponent}". Only way works is to assign skinClass directly in MXML while skinClass ref is still in CSS.
Anybody knows a work around?