[[Raku CSS Project]](https://css-raku.github.io)
/ [[CSS-Properties Module]](https://css-raku.github.io/CSS-Properties-raku)
The CSS::Properties module is a set of related classes for parsing, manipulation and generation of CSS property sets, including inheritance, and defaults.
## Synopsis
```
use CSS::Units :pt;
use CSS::Properties;
my CSS::Properties() $css = "color:red !important; padding: 1pt";
say $css.important("color"); # True
$css.border-color = 'red';
$css.margin = [5pt, 2pt, 5pt, 2pt];
$css.margin = 5pt; # set margin on all 4 sides
# set text alignment
$css.text-align = 'right';
say ~$css; # border-color:red; color:red!important; margin:5pt; padding:1pt; text-align:right;
```
Classes in this module
--------
* [CSS::Properties](https://css-raku.github.io/CSS-Properties-raku/CSS/Properties) - property list manipulation class.
* [CSS::Properties::Calculator](https://css-raku.github.io/CSS-Properties-raku/CSS/Properties/Calculator) - property calculator and measurement tool.
* [CSS::Properties::Optimizer](https://css-raku.github.io/CSS-Properties-raku/CSS/Properties/Optimizer) - property AST optimizer delegate
* [CSS::Properties::PropertyInfo](https://css-raku.github.io/CSS-Properties-raku/CSS/Properties/PropertyInfo) - property meta-data delegate
* [CSS::Box](https://css-raku.github.io/CSS-Properties-raku/CSS/Box) - CSS Box model implementation.
* [CSS::Font](https://css-raku.github.io/CSS-Properties-raku/CSS/Font) - property font manipulation
* [CSS::Font::Descriptor](https://css-raku.github.io/CSS-Properties-raku/CSS/Font/Descriptor) - `@font-face` font descriptor objects
* [CSS::Font::Pattern](https://css-raku.github.io/CSS-Properties-raku/CSS/Font/Pattern) - `@font-face` font patterns and matching
* [CSS::PageBox](https://css-raku.github.io/CSS-Properties-raku/CSS/PageBox) - CSS Box model for paged media
* [CSS::Units](https://css-raku.github.io/CSS-Properties-raku/CSS/Units) - units and post-fix operators (e.g. `12pt`)
See Also
--------
* [CSS](https://css-raku.github.io/CSS-raku/) - Top level CSS manipulation class
## Conformance Levels
Processing defaults to CSS level 3 (class CSS::Module::CSS3). This can be configured via the :module option:
```
use CSS::Properties;
use CSS::Module;
use CSS::Module::CSS1;
use CSS::Module::CSS21;
use CSS::Module::CSS3;
use CSS::Module::SVG;
my $style = 'color:red; azimuth:left;';
my CSS::Module $module = CSS::Module::CSS1.module;
my CSS::Properties $css1 .= new: :$style, :$module;
## warnings: dropping unknown property: azimuth
$module = CSS::Module::CSS21.module;
my CSS::Properties $css21 .= new: :$style, :$module;
## (no warnings)
my CSS::Properties $css3 .= new: :$style; # CSS3 is the default
# -- or --
$module = CSS::Module::CSS3.module;
$css3 .= new: :$style, :$module;
$module = CSS::Module::SVG.module;
$style ~= "paint-order:markers;"; # add a SVG specific property
my CSS::Properties $css-svg .= new: :$style, :$module;
```
`CSS::Module::SVG` is an extension to `CSS::Module::CSS3` that includes
additional SVG specific properties.
### '@font-face' Properties
The [CSS::Font::Descriptor](https://css-raku.github.io/CSS-Properties-raku/CSS/Font/Descriptor) module is a class for managing `@font-face` declarations. The `css` method can be used to get the raw properties.
```
@font-face {
font-family: myFirstFont;
src: url(sansation_light.woff);
}
```
```
use CSS::Properties;
use CSS::Font::Descriptor;
my $style = "font-family: myFirstFont; src: url(sansation_light.woff)";
my CSS::Font::Descriptor $fd .= new: :$style;
my CSS::Properties $font-face-css = $fd.css;
```
## Default values
Most properties have a default value. If a property is reset to its default value it will be omitted from stringification:
my $css = (require CSS::Properties).new;
say $css.background-image; # none
$css.background-image = 'url(camellia.png)';
say ~$css; # "background-image: url(camellia.png);"
$css.background-image = $css.info("background-image").default;
say $css.background-image; # none
say ~$css; # ""
## Deleting properties
Properties can be deleted via the `delete` method, or by assigning the property to `Nil`:
my CSS::Properties $css .= new: :style("background-position:top left; border-top-color:red; border-bottom-color: green; color: blue");
# delete background position
$css.background-position = Nil;
# delete all border colors
$css.delete: "border-color";
## Inheritance
A child class can inherit from one or more parent classes. This follows CSS standards:
- not all properties are inherited by default; for example `color` is, but `margin` is not.
- the `inherit` keyword can be used in the child property to ensure inheritance.
- `initial` will reset the child property to the default value
To inherit a css object or style string:
- pass it as a `:inherit` option, when constructing the object, or
- use the `inherit` method
```
use CSS::Properties;
my $parent-style = "margin-top:5pt; margin-left: 15pt; color:rgb(0,0,255) !important";
my $style = "margin-top:25pt; margin-right: initial; margin-left: inherit; color:purple";
my CSS::Properties $css .= new: :$style, :inherit($parent-style);
say $css.color; # #7F007Frgb (purple)
say $css.handling("margin-left"); # inherit
say $css.margin-left; # 15pt
```
## Optimization and Serialization
method write(
Bool :$optimize = True, # consolidate properties
Bool :$terse = True, # single line output
Bool :$color-names = True, # use color names, where possible
Bool :$keep-defaults = False, # don't omit default values
|c
) is also {
The `.write` (alias `.Str`, or .`gist`) method can be used to produce CSS. Properties are optimized and normalized:
- properties with default values are omitted
- multiple component properties are generally consolidated to container properties (e.g. `font-family: Courier` and `font-size: 12pt`
are consolidated to `font: 12pt Courier`).
- rgb masks are translated to color-names, where possible
```
use CSS::Properties;
my CSS::Properties $css .= new( :style("background-repeat:repeat; border-style: groove; border-width: 2pt 2pt; color: rgb(255,0,0);") );
# - 'border-width' and 'border-style' are consolidated to the 'border' container property
# - rgb(255,0,0) is mapped to 'red'
say $css.write; # "border:2pt groove; color: red;"
```
Notice that:
- `background-repeat` was omitted because it has the default value
- `border-style` and `border-width` have been consolidated to the `border` container property. This is possible
because all four borders have common values
- `color` has been translated from a color mask to a color
`$.write` Options include:
- `:!optimize` - turn off optimization. Don't, combine component properties into container properties (`border-style`, `border-width`, ... => `border`), or combine edges (`margin-top`, `margin-left`, ... => `margin`).
- `:!terse` - enable multi-line output
- `:!color-names` - don't translate RGB values to color-names
See also [CSS::Properties::Optimizer](https://css-raku.github.io/CSS-Properties-raku/CSS/Properties/Optimizer).
## Property Meta-data
The `info` method gives property specific meta-data, on all (component or container properties). It returns an object of type CSS::Properties::PropertyInfo:
```
use CSS::Properties;
use CSS::Properties::PropertyInfo;
my CSS::Properties $css .= new;
my CSS::Properties::PropertyInfo $margin-info = $css.info("margin");
say $margin-info.synopsis; # {1,4}
say $margin-info.edges; # [margin-top margin-right margin-bottom margin-left]
say $margin-info.inherit; # True (property is inherited)
```
## Data Introspection
The `properties` method, gives a list of current properties. Only component properties
are returned. E.g. `font-family` may be returned; but `font` never is.
```
use CSS::Properties;
my $style = "margin-top: 10%; margin-right: 5mm; margin-bottom: auto";
my CSS::Properties $css .= new: :$style;
for $css.properties -> $prop {
my $val = $css."$prop"();
say "$prop: $val {$val.type}";
}
```
Gives:
```
margin-top: 10 percent
margin-bottom: auto keyw
margin-right: 5 mm
```
## Lengths and Units
CSS::Units is a convenience module that provides some simple post-fix length unit definitions.
The `:ops` export overloads `+` and `-` to perform unit
calculations. `+css` and `-css` are also available as
more explicit infix operators:
All infix operators convert to the left-hand operand's units.
```
use CSS::Units :ops, :pt, :px, :in, :mm;
my CSS::Properties $css .= new: :margin[5pt, 10px, .1in, 2mm];
# display margins in millimeters
say "%.2f mm".sprintf(.scale("mm")) for $css.margin.list;
```
The `measure` method can be used to perform contextual measurement
of lengths, which are converted to the default units.
The current font-size is used for `em`, `ex` and percentage calculations.
There are also `viewport-width` and `viewport-height` attributes
that need to be set to enable `vw` and `vh` units.
```
use CSS::Units :ops, :pt, :px, :in, :mm, :em, :vw, :vh, :percent;
use CSS::Properties;
my CSS::Properties $css .= new: :viewport-width(200);
say $css.units; # pt
say $css.measure: 10px; # 7.5pt
say $css.measure: 1in; # 72pt
say $css.font-size; # 12pt
say $css.measure: 2em; # 24pt
say $css.measure: 50%; # 6pt
say $css.measure: .1vw; # 20pt
```
The `measure` method can also be used on specific properties. In the case
of box measurements (borders, margins and padding) a `reference-width` also needs to be set for percentage calculations.
```
use CSS::Units :px, :percent;
use CSS::Properties;
my CSS::Properties $css .= new: :margin[10%, 10px], :reference-width(120);
say $css.measure: :margin-top; # 12pt
say $css.measure: :margin-left; # 7.5pt
say $css.measure: :margin-left(20%); # 24pt
say $css.measure: :font-size; # 12pt
say $css.measure: :font-size(50%); # 6pt
```
The `units` attribute defaults to `pt` can be changed to any absolute length units:
```
use CSS::Units :px, :mm;
use CSS::Properties;
my CSS::Properties $css .= new: :margin[10mm, 10px], :units;
say $css.units; # mm
say $css.measure: :margin-top; # 10mm
say $css.measure: :margin-left; # 2.646mm
```
## Appendix : CSS3 Properties
Name | Default | Inherit | Type | Synopsis
--- | --- | --- | --- | ---
azimuth | center | Yes | | \ \| [[ left-side \| far-left \| left \| center-left \| center \| center-right \| right \| far-right \| right-side ] \|\| behind ] \| leftwards \| rightwards
background | | | hash | ['background-color' \|\| 'background-image' \|\| 'background-repeat' \|\| 'background-attachment' \|\| 'background-position']
background-attachment | scroll | | | scroll \| fixed
background-color | transparent | | | \ \| transparent
background-image | none | | | \ \| none
background-position | 0% 0% | | | [ [ \ \| \ \| left \| center \| right ] [ \ \| \ \| top \| center \| bottom ]? ] \| [ [ left \| center \| right ] \|\| [ top \| center \| bottom ] ]
background-repeat | repeat | | | repeat \| repeat-x \| repeat-y \| no-repeat
border | | | hash,box | [ 'border-width' \|\| 'border-style' \|\| 'border-color' ]
border-bottom | | | hash | [ 'border-bottom-width' \|\| 'border-bottom-style' \|\| 'border-bottom-color' ]
border-bottom-color | the value of the 'color' property | | | \ \| transparent
border-bottom-style | none | | | \
border-bottom-width | medium | | | \
border-collapse | separate | Yes | | collapse \| separate
border-color | | | box | [ \ \| transparent ]{1,4}
border-left | | | hash | [ 'border-left-width' \|\| 'border-left-style' \|\| 'border-left-color' ]
border-left-color | the value of the 'color' property | | | \ \| transparent
border-left-style | none | | | \
border-left-width | medium | | | \
border-right | | | hash | [ 'border-right-width' \|\| 'border-right-style' \|\| 'border-right-color' ]
border-right-color | the value of the 'color' property | | | \ \| transparent
border-right-style | none | | | \
border-right-width | medium | | | \
border-spacing | 0 | Yes | | \ \?
border-style | | | box | \{1,4}
border-top | | | hash | [ 'border-top-width' \|\| 'border-top-style' \|\| 'border-top-color' ]
border-top-color | the value of the 'color' property | | | \ \| transparent
border-top-style | none | | | \
border-top-width | medium | | | \
border-width | | | box | \{1,4}
bottom | auto | | | \ \| \ \| auto
caption-side | top | Yes | | top \| bottom
clear | none | | | none \| left \| right \| both
clip | auto | | | \ \| auto
color | depends on user agent | Yes | | \
content | normal | | | normal \| none \| [ \ \| \ \| \ \| \ \| attr(\) \| open-quote \| close-quote \| no-open-quote \| no-close-quote ]+
counter-increment | none | | | none \| [ \ \? ]+
counter-reset | none | | | none \| [ \ \? ]+
cue | | | hash | [ 'cue-before' \|\| 'cue-after' ]
cue-after | none | | | \ \| none
cue-before | none | | | \ \| none
cursor | auto | Yes | | [ [\ ,]* [ auto \| crosshair \| default \| pointer \| move \| e-resize \| ne-resize \| nw-resize \| n-resize \| se-resize \| sw-resize \| s-resize \| w-resize \| text \| wait \| help \| progress ] ]
direction | ltr | Yes | | ltr \| rtl
display | inline | | | inline \| block \| list-item \| inline-block \| table \| inline-table \| table-row-group \| table-header-group \| table-footer-group \| table-row \| table-column-group \| table-column \| table-cell \| table-caption \| none
elevation | level | Yes | | \ \| below \| level \| above \| higher \| lower
empty-cells | show | Yes | | show \| hide
float | none | | | left \| right \| none
font | | Yes | hash | [ [ \<‘font-style’\> \|\| \ \|\| \<‘font-weight’\> \|\| \<‘font-stretch’\> ]? \<‘font-size’\> [ / \<‘line-height’\> ]? \<‘font-family’\> ] \| caption \| icon \| menu \| message-box \| small-caption \| status-bar
font-family | depends on user agent | Yes | | [ \ \| \ ]\#
font-feature-settings | normal | Yes | | normal \| \\#
font-kerning | auto | Yes | | auto \| normal \| none
font-language-override | normal | Yes | | normal \| \
font-size | medium | Yes | | \ \| \ \| \ \| \
font-size-adjust | none | Yes | | none \| auto \| \
font-stretch | normal | Yes | | normal \| ultra-condensed \| extra-condensed \| condensed \| semi-condensed \| semi-expanded \| expanded \| extra-expanded \| ultra-expanded
font-style | normal | Yes | | normal \| italic \| oblique
font-synthesis | weight style | Yes | | none \| [ weight \|\| style ]
font-variant | normal | Yes | | normal \| none \| [ \ \|\| \ \|\| \ \|\| \ \|\| stylistic(\) \|\| historical-forms \|\| styleset(\ \#) \|\| character-variant(\ \#) \|\| swash(\) \|\| ornaments(\) \|\| annotation(\) \|\| [ small-caps \| all-small-caps \| petite-caps \| all-petite-caps \| unicase \| titling-caps ] \|\| \ \|\| \ \|\| \ \|\| ordinal \|\| slashed-zero \|\| \ \|\| \ \|\| ruby ]
font-variant-alternates | normal | Yes | | normal \| [ stylistic(\) \|\| historical-forms \|\| styleset(\\#) \|\| character-variant(\\#) \|\| swash(\) \|\| ornaments(\) \|\| annotation(\) ]
font-variant-caps | normal | Yes | | normal \| small-caps \| all-small-caps \| petite-caps \| all-petite-caps \| unicase \| titling-caps
font-variant-east-asian | normal | Yes | | normal \| [ \ \|\| \ \|\| ruby ]
font-variant-ligatures | normal | Yes | | normal \| none \| [ \ \|\| \ \|\| \ \|\| \ ]
font-variant-numeric | normal | Yes | | normal \| [ \ \|\| \ \|\| \ \|\| ordinal \|\| slashed-zero ]
font-variant-position | normal | Yes | | normal \| sub \| super
font-weight | normal | Yes | | normal \| bold \| bolder \| lighter \| 100 \| 200 \| 300 \| 400 \| 500 \| 600 \| 700 \| 800 \| 900
height | auto | | | \ \| \ \| auto
left | auto | | | \ \| \ \| auto
letter-spacing | normal | Yes | | normal \| \
line-height | normal | Yes | | normal \| \ \| \ \| \
list-style | | Yes | hash | [ 'list-style-type' \|\| 'list-style-position' \|\| 'list-style-image' ]
list-style-image | none | Yes | | \ \| none
list-style-position | outside | Yes | | inside \| outside
list-style-type | disc | Yes | | disc \| circle \| square \| decimal \| decimal-leading-zero \| lower-roman \| upper-roman \| lower-greek \| lower-latin \| upper-latin \| armenian \| georgian \| lower-alpha \| upper-alpha \| none
margin | | | box | \{1,4}
margin-bottom | 0 | | | \
margin-left | 0 | | | \
margin-right | 0 | | | \
margin-top | 0 | | | \
max-height | none | | | \ \| \ \| none
max-width | none | | | \ \| \ \| none
min-height | 0 | | | \ \| \
min-width | 0 | | | \ \| \
opacity | 1.0 | | | \
orphans | 2 | Yes | | \
outline | | | hash | [ 'outline-color' \|\| 'outline-style' \|\| 'outline-width' ]
outline-color | invert | | | \ \| invert
outline-style | none | | | [ none \| hidden \| dotted \| dashed \| solid \| double \| groove \| ridge \| inset \| outset ]
outline-width | medium | | | thin \| medium \| thick \| \
overflow | visible | | | visible \| hidden \| scroll \| auto
padding | | | box | \{1,4}
padding-bottom | 0 | | | \
padding-left | 0 | | | \
padding-right | 0 | | | \
padding-top | 0 | | | \
page-break-after | auto | | | auto \| always \| avoid \| left \| right
page-break-before | auto | | | auto \| always \| avoid \| left \| right
page-break-inside | auto | | | avoid \| auto
pause | | | | [ [\