[Raku CSS Project]
/ [CSS-Properties Module]
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
See Also
- CSS - Top level CSS manipulation class
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 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:
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<Str gist> {
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.
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; # <margin-width>{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<mm>;
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 | | <angle> | [[ 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 | | | <color> | transparent |
background-image | none | | | <uri> | none |
background-position | 0% 0% | | | [ [ <percentage> | <length> | left | center | right ] [ <percentage> | <length> | 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 | | | <color> | transparent |
border-bottom-style | none | | | <border-style> |
border-bottom-width | medium | | | <border-width> |
border-collapse | separate | Yes | | collapse | separate |
border-color | | | box | [ <color> | transparent ]{1,4} |
border-left | | | hash | [ 'border-left-width' || 'border-left-style' || 'border-left-color' ] |
border-left-color | the value of the 'color' property | | | <color> | transparent |
border-left-style | none | | | <border-style> |
border-left-width | medium | | | <border-width> |
border-right | | | hash | [ 'border-right-width' || 'border-right-style' || 'border-right-color' ] |
border-right-color | the value of the 'color' property | | | <color> | transparent |
border-right-style | none | | | <border-style> |
border-right-width | medium | | | <border-width> |
border-spacing | 0 | Yes | | <length> <length>? |
border-style | | | box | <border-style>{1,4} |
border-top | | | hash | [ 'border-top-width' || 'border-top-style' || 'border-top-color' ] |
border-top-color | the value of the 'color' property | | | <color> | transparent |
border-top-style | none | | | <border-style> |
border-top-width | medium | | | <border-width> |
border-width | | | box | <border-width>{1,4} |
bottom | auto | | | <length> | <percentage> | auto |
caption-side | top | Yes | | top | bottom |
clear | none | | | none | left | right | both |
clip | auto | | | <shape> | auto |
color | depends on user agent | Yes | | <color> |
content | normal | | | normal | none | [ <string> | <uri> | <counter> | <counters> | attr(<identifier>) | open-quote | close-quote | no-open-quote | no-close-quote ]+ |
counter-increment | none | | | none | [ <identifier> <integer>? ]+ |
counter-reset | none | | | none | [ <identifier> <integer>? ]+ |
cue | | | hash | [ 'cue-before' || 'cue-after' ] |
cue-after | none | | | <uri> | none |
cue-before | none | | | <uri> | none |
cursor | auto | Yes | | [ [<uri> ,]* [ 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 | | <angle> | below | level | above | higher | lower |
empty-cells | show | Yes | | show | hide |
float | none | | | left | right | none |
font | | Yes | hash | [ [ <‘font-style’> || <font-variant-css21> || <‘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 | | [ <generic-family> | <family-name> ]# |
font-feature-settings | normal | Yes | | normal | <feature-tag-value># |
font-kerning | auto | Yes | | auto | normal | none |
font-language-override | normal | Yes | | normal | <string> |
font-size | medium | Yes | | <absolute-size> | <relative-size> | <length> | <percentage> |
font-size-adjust | none | Yes | | none | auto | <number> |
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 | [ <common-lig-values> || <discretionary-lig-values> || <historical-lig-values> || <contextual-alt-values> || stylistic(<feature-value-name>) || historical-forms || styleset(<feature-value-name> #) || character-variant(<feature-value-name> #) || swash(<feature-value-name>) || ornaments(<feature-value-name>) || annotation(<feature-value-name>) || [ small-caps | all-small-caps | petite-caps | all-petite-caps | unicase | titling-caps ] || <numeric-figure-values> || <numeric-spacing-values> || <numeric-fraction-values> || ordinal || slashed-zero || <east-asian-variant-values> || <east-asian-width-values> || ruby ] |
font-variant-alternates | normal | Yes | | normal | [ stylistic(<feature-value-name>) || historical-forms || styleset(<feature-value-name>#) || character-variant(<feature-value-name>#) || swash(<feature-value-name>) || ornaments(<feature-value-name>) || annotation(<feature-value-name>) ] |
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 | [ <east-asian-variant-values> || <east-asian-width-values> || ruby ] |
font-variant-ligatures | normal | Yes | | normal | none | [ <common-lig-values> || <discretionary-lig-values> || <historical-lig-values> || <contextual-alt-values> ] |
font-variant-numeric | normal | Yes | | normal | [ <numeric-figure-values> || <numeric-spacing-values> || <numeric-fraction-values> || 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 | | | <length> | <percentage> | auto |
left | auto | | | <length> | <percentage> | auto |
letter-spacing | normal | Yes | | normal | <length> |
line-height | normal | Yes | | normal | <number> | <length> | <percentage> |
list-style | | Yes | hash | [ 'list-style-type' || 'list-style-position' || 'list-style-image' ] |
list-style-image | none | Yes | | <uri> | 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 | <margin-width>{1,4} |
margin-bottom | 0 | | | <margin-width> |
margin-left | 0 | | | <margin-width> |
margin-right | 0 | | | <margin-width> |
margin-top | 0 | | | <margin-width> |
max-height | none | | | <length> | <percentage> | none |
max-width | none | | | <length> | <percentage> | none |
min-height | 0 | | | <length> | <percentage> |
min-width | 0 | | | <length> | <percentage> |
opacity | 1.0 | | | <number> |
orphans | 2 | Yes | | <integer> |
outline | | | hash | [ 'outline-color' || 'outline-style' || 'outline-width' ] |
outline-color | invert | | | <color> | invert |
outline-style | none | | | [ none | hidden | dotted | dashed | solid | double | groove | ridge | inset | outset ] |
outline-width | medium | | | thin | medium | thick | <length> |
overflow | visible | | | visible | hidden | scroll | auto |
padding | | | box | <padding-width>{1,4} |
padding-bottom | 0 | | | <padding-width> |
padding-left | 0 | | | <padding-width> |
padding-right | 0 | | | <padding-width> |
padding-top | 0 | | | <padding-width> |
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 | | | | [ [<time> | <percentage>]{1,2} ] |
pause-after | 0 | | | <time> | <percentage> |
pause-before | 0 | | | <time> | <percentage> |
pitch | medium | Yes | | <frequency> | x-low | low | medium | high | x-high |
pitch-range | 50 | Yes | | <number> |
play-during | auto | | | <uri> [ mix || repeat ]? | auto | none |
position | static | | | static | relative | absolute | fixed |
quotes | depends on user agent | Yes | | [<string> <string>]+ | none |
richness | 50 | Yes | | <number> |
right | auto | | | <length> | <percentage> | auto |
size | auto | | | <length>{1,2} | auto | [ <page-size> || [ portrait | landscape] ] |
speak | normal | Yes | | normal | none | spell-out |
speak-header | once | Yes | | once | always |
speak-numeral | continuous | Yes | | digits | continuous |
speak-punctuation | none | Yes | | code | none |
speech-rate | medium | Yes | | <number> | x-slow | slow | medium | fast | x-fast | faster | slower |
stress | 50 | Yes | | <number> |
table-layout | auto | | | auto | fixed |
text-align | a nameless value that acts as 'left' if 'direction' is 'ltr', 'right' if 'direction' is 'rtl' | Yes | | left | right | center | justify |
text-decoration | none | | | none | [ underline || overline || line-through || blink ] |
text-indent | 0 | Yes | | <length> | <percentage> |
text-transform | none | Yes | | capitalize | uppercase | lowercase | none |
top | auto | | | <length> | <percentage> | auto |
unicode-bidi | normal | | | normal | embed | bidi-override |
vertical-align | baseline | | | baseline | sub | super | top | text-top | middle | bottom | text-bottom | <percentage> | <length> |
visibility | visible | Yes | | visible | hidden | collapse |
voice-family | depends on user agent | Yes | | [<generic-voice> | <specific-voice> ]# |
volume | medium | Yes | | <number> | <percentage> | silent | x-soft | soft | medium | loud | x-loud |
white-space | normal | Yes | | normal | pre | nowrap | pre-wrap | pre-line |
widows | 2 | Yes | | <integer> |
width | auto | | | <length> | <percentage> | auto |
word-spacing | normal | Yes | | normal | <length> |
z-index | auto | | | auto | <integer> |
The above markdown table was produced with the following code snippet
use v6;
say <Name Default Inherit Type Synopsis>.join(' | ');
say ('---' xx 5).join(' | ');
my $css = (require CSS::Properties).new;
for $css.properties(:all).sort -> $name {
with $css.info($name) {
my @type;
@type.push: 'hash' if .children;
@type.push: 'box' if .box;
my $synopsis-escaped = .synopsis.subst(/<?before <[ < | > # ]>>/, '\\', :g);
say ($name,
.default // '',
.inherit ?? 'Yes' !! '',
@type.join(','),
$synopsis-escaped,
).join(' | ');
}
}