Custom CSS Property

From PowerUI
Revision as of 21:48, 12 March 2017 by 188.222.158.94 (talk) (Formal Syntax and specifications)
Jump to: navigation, search

Custom CSS properties can be a great way to reuse functionality and extend PowerUI. They're designed to be simple to make, so simple copy and pasting skills or basic coding skills can be all that's required. Let's get started!

A basic property

The most basic CSS property (A C# file) looks like this:

using Css;

// hello-world
public class HelloWorld: CssProperty{
    
    public override string[] GetProperties(){
    
        // One or more names (sometimes prefixed) that your property handles:
        return new string[]{"hello-world"};
        
    }
    
}

At the very least, you must:

  • Inherit from an appropriate type of CSS property (it'll almost always be CssProperty that you use)
  • Give it one or more CSS property names.
  • Give it a class name, which is typically just the main name of the property.
  • Optionally put it in the Css.Properties namespace.

Place it somewhere outside the PowerUI folders. This is just incase you update PowerUI, as you won't want to loose your changes. It can go anywhere you find appropriate.

Initial value and inheritance

Typically you'll want to define an initial value (default none) and if your property inherits or not (default no). That's done in the constructor:

// Constructor for the above CSS property
public HelloWorld(){
    
    // Either set it as a Css value:
    // (such as the common 'auto' one, made available as AUTO for convenience)
    // InitialValue=AUTO;
    
    // Or as text:
    InitialValueText="0 50%";
    
    // Does it inherit?
    Inherits=true;
    
}

Aliases

An aliased property is once which updates the internal value of some other set value. For example 'scroll-top' is an alias for scroll[0]. In CSS there are 4 main types:

  • Point aliases. property-x and property-y.
  • Colour aliases. property-r, property-g, property-b and property-a.
  • Square aliases. property-top, property-left, property-bottom, property-right.
  • Logical aliases. property-block-start, property-inline-end, property-block-end, property-inline-start.

Note that they alias in the order seen above. I.e. property-b is property[3]. To setup these common aliases:

// Called to setup aliases.
public override void Aliases(){
    
    // Only call the one(s) you actually want
    // (you _could_ call them all, but it would probably be confusing!)
    
    // Point (x,y):
    // PointAliases2D();
    
    // Point (x,y,z):
    // PointAliases3D();
    
    // Colour:
    // ColourAliases();
    
    // Square (top,left,bottom,right) *and* logical:
    // SquareAliases();
    
    // Something else - see below for more!
    Alias("some-random-css-property",ValueAxis.None,0);
}

Above we're also mapping 'some-random-css-property' to the 0th index of 'this' property (the one that -x, -top, -r etc also map to). You also specify which axis it uses too - that's important for resolving %.

For example, text-indent is an alias of padding[3] (the same as padding-left). If you used a % in text-indent, you would expect it to be a % of the width (the x or horizontal axis). To make that real, take a look at padding.cs where you'll see this:

// text-indent maps to padding-left, aka index 3 of padding:
Alias("text-indent",ValueAxis.X,3);

Composite Properties

Composite properties set multiple CSS properties all at once. background, border, animation etc are all examples of composite properties. In this case, you derive from CssCompositeProperty instead, and either define the formal syntax as a specification or manually parse your value.

Here's how we define a formal syntax:

using Css;

// hello-composition basics (using a spec)
public class HelloComposition : CssCompositeProperty{
   
    // GetProperties is the same:
    public override string[] GetProperties(){
        return new string[]{"hello-composition"};
    }
    
    // Define the specification:
    protected override Spec.Value GetSpecification(){
        
        // Let's define the composite formal syntax as this:
        /*
          [hello-composition-height || hello-composition-style]
        */
        
        // To represent the above, we declare it like so:
        return new Spec.AnyOf(
            new Spec.Property(this, "hello-composition-height"),
            new Spec.Property(this, "hello-composition-style")
        );
       
    }
    
}

For this specification form to work, you must also declare GetSpecification on all of the properties that are referenced. In the example above, that means you'd need to define both hello-composition-height and hello-composition-style as normal properties, but also have GetSpecification on both of them. The specification of those properties simply depends on which values they accept.

Simple composite example: text-decoration (and the two properties it references) Complex composite example: font


Formal Syntax and specifications

(If you're wondering what these are, take a look at, for example, the MDN article for text-decoration).

When converting a standard CSS formal syntax into a spec object, use this handy cheatsheet:

Operator Class to use
a || b Spec.AnyOf
a b Spec.All
<a> Spec.Property
a? Spec.Optional
a | b Spec.OneOf
/ Spec.Literal
a && b Spec.AllAnyOrder
{1,4} Spec.Repeated
a* Spec.Repeated (use 0,Infinity)
a+ Spec.Repeated (use 1,Infinity)
<number>, <percent> etc Spec.ValueType(typeof(Css.Units.PercentUnit))

Declaring a specification is generally convenient as that's how they're actually defined. However, if you want to parse the value in a custom way (there are a few properties which can't be defined using the web specification format), use OnReadValue instead. animation is an example of a CSS property that does that.

Knowing when the computed value of your property changed

Whenever a CSS property is recomputed, it fires the Apply method:

public override ApplyState Apply(ComputedStyle style,Value value){
    
    // This property has changed on the given computed style!
    // Properties that render things add or remove a Renderer to the renderable data.
    // Take a look at the background-color property for example.
    
    // Unless you're caching the value as something else, 
    // you'll almost always just return Ok:
    return ApplyState.Ok;
}

Finding built-in properties

Currently there are 3 major groups of CSS properties in PowerUI:

  • Core - Path/To/PowerUI/Spark/Properties
  • SVG extensions - Path/To/PowerUI/Source/SVG/Css Properties
  • MathML extensions - Path/To/PowerUI/Source/Extras/MathML/Css Properties

Checking your property is available

An easy sanity check is to make sure your new property is being correctly detected by PowerUI. To do that:

  • Go to Window > PowerUI > Supported CSS
  • Look for e.g. your-property-name

If PowerUI is successfully detecting your property, it will be visible on this list.