Functional and Design Specification


Glossary


Halo The default skins used by Flex 2 and Flex 3
Spark The default skins used by Flex 4

Summary and Background


Styles for the Spark skins are designed to be simple, predictable, and consistent. You can change the entire appearance of an application with a few global style properties. This is a stark contrast to styling in Halo where you have many more knobs to adjust the appearance, and many of these knobs need to be set on individual components or type selectors.

The problem is many simple and common tasks that could be accomplished with styles in Halo now require re-skinning in Spark. This feature adds more styles to our Spark skins.

We are not striving for complete parity with Halo styles. The goal is to expose the most commonly requested styles. Skinning is still the "ultimate fallback" for customization that cannot be achieved through styling.

Usage Scenarios


Peter wants to change the appearance of his app to make the corners of Buttons more rounded, and to make the background of TextInput controls have some translucency. Previously this would require copying and pasting the entire skin files and tweaking the graphics markup accordingly. Now, thanks to this new feature, these tweaks can be done simply by styling.

Detailed Description


The new styles fall into two buckets:

  • Global inheriting styles This is where all of the existing Spark styles live. This bucket is used to easily and consistently change the appearance of the entire application by simply setting a few global style properties. The default values for these styles are never defined on type selectors. They are always set on the single global selector.
  • Local non-inheriting styles This is how many of the Halo styles are handled. These are typically defined on individual type selectors, although some may be defined globally.

An additional consideration is "backgroundColor" for the Spark Application, WindowedApplication, and Window components. This is currently a property, but will become a style.

Related features

Before digging into the new styles, let's take a look at some related features first.

Border

The Border component (working name) will be a new Spark SkinnableContainer subclass that supports most of the old Halo Container styles. Border is used to add borders, backgrounds, and drop shadows to content. We're still working on the details for this component, but we expect it to support all of the common Container styles like borderThickness, cornerRadius, backgroundColor, backgroundAlpha, borderColor, backgroundImage, and dropShadowVisible. Alternatively you can set the stroke and fill objects directly for even more control over the appearance. This component is not yet available.

Advanced CSS

There are many Halo styles that exist purely to style sub-components or states of a component. Examples of sub-component styles include headerStyleName on Accordion, dropDownStyleName on ComboBox and verticalScrollBarStyleName on List. Examples of state-specific styles include textRollOverColor, disabledIcon, and selectedTabTextStyleName.

The new advanced CSS features in Flex 4 obviate the need for these types of styles. Here are some examples of using the new CSS syntax:

<fx:Style>
    @namespace s "library://ns.adobe.com/flex/spark";

    <!-- Using an id selector to set the font size for Panel title -->
    s|Panel #titleDisplay {
        fontSize: 20;
    }

    <!-- Using descendant selectors to set the chromeColor for scroll bars inside TextAreas -->
    s|TextArea s|ScrollBar {
        chromeColor: #CCCCDD;
    }

    <!-- Using pseudo selectors to change the color of the checkbox checkmark in the "downAndSelected" state -->
    s|CheckBox:downAndSelected {
        symbolColor: #FFFF00;
    }

    <!-- These can all be combined as needed. Here is a silly example that sets the chromeColor of the down state
         of the increment button in a scroll bar inside a text area. Whew! -->
    s|TextArea s|ScrollBar #incrementButton:down {
        chromeColor: #FF0000;
    }
    
</fx:Style>

As you can see, the new advanced CSS features provide far more flexibility and styling options than the old Halo styles. For more information about advanced CSS, see the CSS Advanced Selectors Spec.

Existing Spark style properties

The existing Spark style properties are chromeColor, contentBackgroundColor, focusColor, and symbolColor (Spark skins also share the alternatingItemColors, color, rollOverColor, and selectionColor styles with Halo, along with all text related styles). See the Styling Gumbo Components spec for details.

New proposed Spark style properties

Here is a list of new style properties for the Spark skins.

Global inheriting styles

Style Description
accentColor Additional color used for accent. This color is used by "emphasized" buttons, and the slider track highlight.
contentBackgroundAlpha alpha transparency for any background area colored by contentBackgroundColor

Local non-inheriting styles

Style Component(s) Description
backgroundAlpha Window, WindowedApplication alpha of background
backgroundColor Application, Window, WindowedApplication color of background
borderAlpha List, TextInput, TextArea, TabNavigator, Accordion, Panel, DateChooser Alpha of border.
borderColor List, TextInput, TextArea, TabNavigator, Accordion, Panel, DateChooser Color of border. Issue: how does this relate to chromeColor?
borderVisible List, TextInput, TextArea, TabNavigator, Accordion, Panel, DateChooser Visibility of border
cornerRadius Button, ToggleButton, DropDownList, ComboBox, NumericStepper, Panel, PopUpButton, Spinner, Tab, DateChooser Radius of rounded corners
dropShadowVisible Panel, DropDownList, ComboBox, Menu Controls visibility of drop shadow
focusAlpha all focusable components alpha for the focus ring
focusBlendMode all focusable components blend mode of focus ring. Useful for accessibility
focusThickness all focusable components weight of the focus ring, in pixels
paddingBottom TextInput, TextArea Bottom padding
paddingLeft TextInput, TextArea Left padding
paddingRight TextInput, TextArea Right padding
paddingTop TextInput, TextArea Top padding

The List styles also apply to subclasses of List: Tree, DataGrid, HorizontalList, TileList

Note about cornerRadius

The cornerRadius style is a single value. Each skin is responsible for rounding the appropriate corners. For example, the NumericStepper skin picks rounds the right corners of the spinner buttons, but no the left corners. There are no styles for individual corner rounding.

Halo styles and Spark counterparts

This table shows several MX components along with their Halo style properties and Spark style counterparts. There are many styles not listed here, including text styles, constraints, and layout-related styles. These are theme-agnostic styles that will work regardless of the skin or theme applied to the component.

Button

Halo style Spark style Notes
borderColor chromeColor chromeColor applies to the entire button, not just border
cornerRadius cornerRadius  
fillAlphas none  
fillColors chromeColor chromeColor applies to the entire button, not just the fill
focusAlpha focusAlpha  
focusBlendMode focusBlendMode  
focusRoundedCorners none Spark focus skin doesn't need this style
focusThickness focusThickness  
highlightAlphas none no Spark equivalent
themeColor chromeColor, focusColor, advanced CSS Halo themeColor is used for several things including the background of the "down" state, the focus ring, and the border of the "over" state. In Spark, these are handled differently. You can color individual states using CSS, and the focus color is handled independently.

List

Halo style Spark style Notes
alternatingItemColors alternatingItemColors  
backgroundAlpha contentBackgroundAlpha  
backgroundColor contentBackgroundColor  
backgroundDisabledColor none use advanced CSS
backgroundImage none  
backgroundSize none  
borderColor borderColor  
borderSides none border is always on all sides
borderStyle none  
borderThickness borderVisibility Can turn border on and off only
cornerRadius none  
dropShadowColor none no drop shadow support on List
dropShadowEnabled dropShadowVisible  
focusAlpha focusAlpha  
focusThickness focusThickness  
focusBlendMode focusBlendMode  
rollOverColor rollOverColor  
selectionColor selectionColor  
shadowDirection none  
shadowDistance none  
themeColor chromeColor, rollOverColor, selectionColor, focusColor The Spark skins have individual control over the various bits colorized by themeColor in Halo.

Slider

Halo style Spark style Notes
borderColor chromeColor chromeColor applies to the entire Slider, not just the border
fillAlphas none  
fillColors chromeColor chromeColor applies to the entire Slider, not just the fill
focusBlendMode focusBlendMode  
focusThickness focusThickness  
themeColor chromeColor, focusColor  
trackColors chromeColor use advanced CSS to target the track

Adding support for binding to getStyle()

In order for the compiler to handle binding to getStyle(), support for a new Bindable metadata variant will be added:

[Bindable(style="true")]

This will be put on UIComponent's and TextGraphicElement's getStyle(). When the compiler finds a function in a data binding expression marked with this metadata, it generates a FunctionReturnWatcher without the requirement that child watchers exist and it sets the isStyle constructor parameter to true. The isStyle parameter signals to the FunctionReturnWatcher that is should listen for style change events.

In conjunction with the FunctionReturnWatcher changes, UIComponent.stylesChanged() will be updated to dispatch "<style>Changed" events or "allStylesChanged" events when all the styles for a component have changed.

Adding support for picking up the backgroundColor style off of the root Application, even if it is defined in CSS.

PreLink will be updated to search the root document's StyleContainer and the global StyleContainer for a class selector or type selector which specifies the backgroundColor. The same precedence rules as runtime lookup of the backgroundColor will apply. If found, it will be used in the same way as if the backgroundColor was set via the command line, -default-background-color. This includes setting the background color in the SWF using the SetBackgroundColor SWF tag and in HTML with the Embed tag.

API Description


See the Detailed Description section for information about which components honor these styles.

{
    //--------------------------------------------------------------------------
    //
    //  Inheriting styles
    //
    //--------------------------------------------------------------------------

    /**
     *  The alpha transparency for the content background of the component.
     *
     *  @default 1.0
     */
    [Style(name="contentBackgroundAlpha", type="Number", inherit="yes", theme="spark")]


    //--------------------------------------------------------------------------
    //
    //  Non-inheriting styles
    //
    //--------------------------------------------------------------------------

    /**
     *  The alpha transparency for the background of the component.
     *
     *  @default 1.0
     */
    [Style(name="backgroundAlpha", type="Number", inherit="no", theme="spark")]


    /**
     *  The color for the background of the component.
     *
     *  @default #FFFFFF
     */
    [Style(name="backgroundColor", type="uint", format="Color", inherit="no", theme="halo, spark")]


    /**
     *  The alpha for the border of the component.
     *
     *  @default 1.0
     */
    [Style(name="borderAlpha", type="Number", inherit="no", theme="spark")]


    /**
     *  The color for the border of the component.
     *
     *  @default #686868
     */
    [Style(name="borderColor", type="uint", format="Color", inherit="no", theme="halo, spark")]


    /**
     *  The visibility of the components border.
     *
     *  @default true
     */
    [Style(name="borderVisible", type="Boolean", inherit="no" theme="spark")]


    /**
     *  The radius of the corners of the component.
     *
     *  @default 2.0
     */
    [Style(name="cornerRadius", type="Number", inherit="no" theme="halo, spark")]


    /**
     *  The visibility of the components drop shadow.
     *
     *  @default true
     */
    [Style(name="dropShadowVisible", type="Boolean", inherit="no", theme="halo, spark")]


    /**
     *  The alpha of the focus ring.
     *
     *  @default 0.55
     */
    [Style(name="focusAlpha", type="Number", inherit="no", theme="halo, spark")]


    /**
     *  The blend mode of the focus ring.
     *
     *  @default "normal"
     */
    [Style(name="focusBlendMode", type="String", inherit="no", theme="halo, spark")]


    /**
     *  The thickness of the focus ring.
     *
     *  @default 2
     */
    [Style(name="focusThickness", type="Number", inherit="no", theme="halo, spark")]


    /**
     *  The bottom padding for this component.
     *
     *  @default 3
     */
    [Style(name="paddingBottom", type="Number", inherit="no", theme="spark")]


    /**
     *  The left padding for this component.
     *
     *  @default 3
     */
    [Style(name="paddingLeft", type="Number", inherit="no", theme="spark")]


    /**
     *  The right padding for this component.
     *
     *  @default 3
     */
    [Style(name="paddingRight", type="Number", inherit="no", theme="spark")]


    /**
     *  The top padding for this component.
     *
     *  @default 5
     */
    [Style(name="paddingTop", type="Number", inherit="no", theme="spark")]
}

B Features


None.

Examples and Usage


Setting the contentBackgroundAlpha for all components

<fx:Style>
    global {
        contentBackgroundAlpha: 0.6;
    }
</fx:Style>

Setting the padding for all TextInput controls

<fx:Style>
    @namespace s "library://ns.adobe.com/flex/spark";

    s|TextInput {
        paddingLeft: 10;
        paddingRight: 10;
        paddingTop: 4;
        paddingBottom: 4;
    }
</fx:Style>

Setting cornerRadius on an individual Button

<s:Button label="Submit" cornerRadius="5" />

Setting the chromeColor for all Buttons to red using ActionScript

<fx:Script>
    <[CDATA[

    var cssR:CSSStyleDeclaration = StyleManager.getStyleDeclaration("spark.components.Button");
    cssR.setStyle("chromeColor", "red");
]]>
</fx:Script>

Additional Implementation Details


In order to simplify the implementation we are adding binding support to the getStyle() function. This means you will be able to bind directly to style properties in your markup:

<s:Rect ...>
        <s:stroke>
            <s:SolidColorStroke color="{getStyle('borderColor')}" ... />
        </s:stroke>
    </s:Rect>

Prototype Work


Geometry altering styles like borderVisibility need to be prototyped to determine the best plan for implementation.

Compiler Work


  • Adding support for binding to getStyle()
  • Adding support for picking up the backgroundColor style off of the root Application, even if it is defined in CSS.

Web Tier Compiler Impact


None.

Flex Feature Dependencies


The new components and skins planned for 4+ need to be accounted for.

Backwards Compatibility


Syntax changes

None.

Behavior

None.

Warnings/Deprecation

None.

Accessibility


The focusBlendMode style is primarily for accessibility. This was supported by the Halo skins, and will now be supported by the Spark skins.

Performance


Need to make sure these additional styles don't have too much impact on performance and memory. Specifically, binding to getStyle() vs. pushing in style properties directly.

Globalization


None.

Localization


Compiler Features

None.

Framework Features

None.

Issues and Recommendations


None.

Documentation


Many of the styles behave identically to their Halo counterpart. There may be some differences that need to be called out in the documentation.

QA


All of the usual style testing guidelines apply here. Need to test global, type, and class selectors along with setting inline via MXML and ActionScript.


Hey,

I just redesigned the StyleManager to support nested target/property styling, making it completely dynamic. I began trying to create custom Style metadata and such about 6 months ago with Spark when I wanted to create my own skins, but I quickly found it had some very severe limitations:

1) If I want to add new styleable properties to core components, I have to extend the core components and add metadata, which, even I did this (super bad practice), I would have to:
2) Write a custom way to translate those styleable properties into some graphical manifestation: For each Style property, I'd have to write how to handle it. That is WAY too much work because
3) There are over 50 components, and I'd have to spend weeks styling them, and I have days and hours at a time to get the job done.
4) In the end, all properties should be styleable, I think it's just a leftover HTML mindset that says only "these" properties are styleable. I'm currently styling everything: Fills, GradientEntries, Strokes, Layouts, etc. And what your post shows is that we actually need to style properties: cornerRadius is a property of a Rectangle or something, but what about the topLeftCornerRadius, and what if I wanted to style each state of each style property! It would be nuts.

In spending months on this problem, I have realized that the implementation of all styleable properties can be handled by just creating a property on your component, or by using the already-defined properties on the components in flex and other libraries. Like if I wanted to style the PanelSkin-highlight-fill-gradientEntry-alpha, and then the ComboBox-dropShadow-blurX, I'd have to write all kinds of unreusable code. Or create a billion metadata tags, which is very undesirable.

So I don't think it's a good idea to ask "is this the right set of styles?", because thats a requirement that will change from project to project, component to component, person to person. I think there should be no Style Metadata. Take the LayoutBase for example: they're starting to define accessors for all the VGroup Styles (padding, gap...)! PLUS, when if I want to not only style the "background" in the skin, but the "foreground", the "highlight", and the "bottomRightRectangle", then following the current convention, I would end up with something like this:

[Style(name="backgroundColor", type="uint", format="Color", inherit="yes", theme="spark")]
[Style(name="backgroundAlpha", type="uint", format="Color", inherit="yes", theme="spark")]
[Style(name="backgroundGradientType", type="string", inherit="no", theme="spark")]
[Style(name="backgroundTopRightBorderRadius", type="Number", inherit="yes", theme="spark")]
[Style(name="backgroundTopLeftBorderRadius", type="Number", inherit="yes", theme="spark")]
[Style(name="backgroundBottomRightBorderRadius", type="Number", inherit="yes", theme="spark")]
[Style(name="backgroundBottomLeftBorderRadius", type="Number", inherit="yes", theme="spark")]

[Style(name="highlightColor", type="uint", format="Color", inherit="yes", theme="spark")]
[Style(name="highlightAlpha", type="uint", format="Color", inherit="yes", theme="spark")]
[Style(name="highlightGradientType", type="string", inherit="no", theme="spark")]
[Style(name="highlightTopRightBorderRadius", type="Number", inherit="yes", theme="spark")]
[Style(name="highlightTopLeftBorderRadius", type="Number", inherit="yes", theme="spark")]
[Style(name="highlightBottomRightBorderRadius", type="Number", inherit="yes", theme="spark")]
[Style(name="highlightBottomLeftBorderRadius", type="Number", inherit="yes", theme="spark")]

[Style(name="bottomRectangleColor", type="uint", format="Color", inherit="yes", theme="spark")]
[Style(name="bottomRectangleAlpha", type="uint", format="Color", inherit="yes", theme="spark")]
[Style(name="bottomRectangleGradientType", type="string", inherit="no", theme="spark")]
[Style(name="bottomRectangleTopRightBorderRadius", type="Number", inherit="yes", theme="spark")]
[Style(name="bottomRectangleTopLeftBorderRadius", type="Number", inherit="yes", theme="spark")]
[Style(name="bottomRectangleBottomRightBorderRadius", type="Number", inherit="yes", theme="spark")]
[Style(name="bottomRectangleBottomLeftBorderRadius", type="Number", inherit="yes", theme="spark")]

public class CustomStyledPanel extends spark.components.Panel ...

... And that's only scratching the surface of everything you'd like to have control over, and I'd have to have a bunch of stuff handling all that in "stylesChanged" or whatever. Nobody wants to do that. Plus I'm extending classes which is unnecessary. And if I want states, then multiply the above code block by 4. Check out the Degrafa CSS for what this will begin to look like, and they are only styling a single rectangle! It's HUGE!

I propose just defining a property in the StyleManager, or in the CSS selector, that tells you:

1) What the styleElements are (background, highlight, dropShadowRectangle, etc.), which are anything you want, as long as they're already in the skin (spark skins have like 7 rectangles you can create custom fills and strokes on). It'd take a bit more work to be able to add visual elements through css, but it can be done pretty easily.
2) What the styleProperties are (background-fill-alpha, etc.). These can be mapped in Dictionaries or Token objects to styleNames, classes, instances, advanced selectors, etc, and the "setStyle" method would add one extra thing to setting the property value on its target: it would pass the style down to the children if "inherit" was true (could be a parameter to a setStyleProperties(target, inheritable) method, or you could use UIComponent's inheritableStyles and nonInheritableStyles properties somehow.

That's basically what I'm doing now and it saves me TONS of time, and it does everything the Styles in Flex do now, plus a ton more as described here. I can completely change every aspect of all 50+ new Flex components in a single CSS file, I can dynamically add however many GradientEntries I want, so things can have 2 or 7 gradient entries, and I can easily incorporate transitions between states, all through CSS.

In short, it's a long and dead-end road to start manually defining each property you want to style in a Metadata tag, defining how it's interpreted, etc. That's hardcoding to the extreme, and it's not easy for newcomers to customize anything.

Best,
Lance

Hi Lance,

Styling though CSS selectors does not require adding any metadata to the component. The metadata is only needed for setting inline style values (ie <s:Button cornerRadius="6" .../>), and for documentation.

I agree that the metadata should be added to skins instead of components, but unfortunately it doesn't look like we'll be able to get that in for this release.

I'd be interested in seeing your prototype. How well does it perform? How do you look up the elements in the skin? Are you using id or some other method?

Glenn