[[Raku CSS Project]](https://css-raku.github.io)
 / [[CSS-Module]](https://css-raku.github.io/CSS-Module-raku)

# CSS::Module

```
# Parse a sample stylesheet as CSS 2.1. Dump the AST.
use v6;
use CSS::Module::CSS21;
my $css = 'h1 { color: orange; text-align: center }';
my $module = CSS::Module::CSS21.module;
my $actions = $module.actions.new;
$module.grammar.parse( $css, :$actions);
say $/.ast.raku;
```

CSS::Module is a set of Raku classes for parsing and manipulation of CSS Levels 1, 2.1 and  3, and SVG.

It contains modules `CSS::Module::CSS1`, `CSS::Module::CSS21` and `CSS::Module::CSS3` for CSS levels 1.0, 2.1 and 3.0, along with `CSS::Module::SVG`, which is a CSS3 extension for [styling SVG](https://www.w3.org/TR/SVG2/styling.html).

Each of these classes has a `module` method which produces an object that encapsulates grammars, actions
and property metadata. It has a`property-metadata` method that can be used to introspect properties. For example

```
% raku -M CSS::Module::CSS3 -e'say CSS::Module::CSS3.module.property-metadata<azimuth>.raku'
{:default("center"), :inherit, :synopsis("<angle> | [[ left-side | far-left | left | center-left | center | center-right | right | far-right | right-side ] || behind ] | leftwards | rightwards")}
```

Note: `CSS::Module::CSS3.module` is composed from the following grammars.

- `CSS::Module::CSS3::Colors`     - CSS 3.0 Colors (@color-profile)
- `CSS::Module::CSS3::Fonts`      - CSS 3.0 Fonts (@font-face)
- `CSS::Module::CSS3::Selectors`  - CSS 3.0 Selectors
- `CSS::Module::CSS3::Namespaces` - CSS 3.0 Namespace (@namespace)
- `CSS::Module::CSS3::Media`      - CSS 3.0 Media (@media)
- `CSS::Module::CSS3::PagedMedia` - CSS 3.0 Paged Media (@page)
- `CSS::Module::CSS3::Value_and_Units` - CSS 3.0 Base units and rules, plus calc() expressions
- `CSS::ModuleX::CSS21`           - the full set of CSS21 properties

This corresponds to the sub-modules described in [CSS Snapshot 2010](http://www.w3.org/TR/2011/NOTE-css-2010-20110512/).

## Installation

You can use the Raku `zef` module installer to test and install `CSS::Module`:

    % zef install CSS::Module

## Examples

- parse a stylesheet using the CSS2.1 grammar:

    % raku -MCSS::Module::CSS21 -e"say CSS::Module::CSS21.parse('h1 {margin:2pt; color: blue}')"


- compile a CSS2.1 stylesheet to an AST, using the module interface:

```
use v6;
use CSS::Module::CSS21;

my $css = 'H1 { color: blue; foo: bar; background-color: zzz }';
my $module  = CSS::Module::CSS21.module;
my $grammar = $module.grammar;
my $actions = $module.actions.new;
my $p = $grammar.parse($css, :$actions);
note $_ for $actions.warnings;
say "declaration: " ~ $p.ast[0]<ruleset><declarations>.raku;
# output:
# unknown property: foo - declaration dropped
# usage background-color: <color> | transparent | inherit
# declaration: {"color" => {"expr" => [{"rgb" => [{"num" => 0}, {"num" => 0}, {"num" => 255}]}]}
```

- parse an individual `azimuth` property expression via the module interface:

```
    use v6;
    use CSS::Module::CSS21;
    my $ast = CSS::Module::CSS21.module.parse-property('azimuth', 'center-left behind');
```

- Composition: A secondary aim is mixin style module composition. For example to create a module MyCSS3Subset::CSS3 comprising CSS2.1 properties + CSS3 Selectors + CSS3 Colors:

    ```
    use v6;

    use CSS::Module;
    use CSS::Module::CSS21::Actions;
    use CSS::Module::CSS21;

    use CSS::Module::CSS3::Selectors;
    use CSS::Module::CSS3::Colors;
    use CSS::Module::CSS3::_Base;

    class MyCSS3Subset::Actions
        is CSS::Module::CSS3::Selectors::Actions
        is CSS::Module::CSS3::Colors::Actions
        is CSS::ModuleX::CSS21::Actions
        is CSS::Module::CSS3::_Base::Actions {
    };
    grammar MyCSS3Subset::CSS3
        is CSS::Module::CSS3::Selectors
        is CSS::Module::CSS3::Colors
        is CSS::ModuleX::CSS21
        is CSS::Module::CSS3::_Base {

        #| a minimal module definition: grammar + actions
        method module {
	  state $this //= CSS::Module.new(
              :name<my-css3-subset>,
              :grammar($?CLASS),
              :actions(MyCSS3Subset::Actions) );
        }
    };

    ```

## Property Definitions

Property definitions are built from the sources in the `src` directory using the CSS::Specification tools. These implement the [W3C Property Definition Syntax](https://developer.mozilla.org/en-US/docs/Web/CSS/Value_definition_syntax).

These also require use of CSS::Specification::Compiler to build the modules. This uses the experimental RakuAST
API to compile the definitions, and has not yet been released to the Raku ecosystem. It can be checked out
from https://github.com/css-raku/CSS-Specification-Compiler-raku.git and added to the Raku library path.

See `make-modules.pl`.

## Actions Options

- **`:lax`** Don't warn about, or discard, unknown properties, sub-rules. Pass back the elements with a classification
of unknown. E.g.
```
    my $module  = CSS::Module::CSS21.module;
    my $grammar = $module.grammar;
    my $actions = $module.actions.new( :lax );

    say $grammar.parse('{bad-prop: 12mm}', :$actions, :rule<declarations>).ast.raku;
    # output {"property:unknown" => {:expr[{ :mm(12) }], :ident<bad-prop>}}

    say $grammar.parse('{ @guff {color:red} }', :$actions, :rule<declarations>).ast.raku;
    # output: {"margin-rule:unknown" =>  { :declarations[ { :ident<color>,
                                                          :expr[ { :rgb[ { :num(255) }, { :num(0) }, { :num(0) } ] } ] } ],
                                         :at-keyw<guff> } }
```
`lax` mode likewise returns quantities with unknown dimensions:
```
    say $grammar.parse('{margin: 12mm .1furlongs}', :$actions, :rule<declarations>).ast.raku;
    # output {"property" => {:expr[{ :mm(12) }, { :num(0.12), "units:unknown" => <furlongs>}], :ident<margin>}}
```

## Custom Properties

Properties may be added, or overriden via an `:%extensions` option to the `new()` method.

```
subset MyAlignment of Str where 'left'|'middle'|'right';
sub coerce(MyAlignment:D $keyw --> Pair) { :$keyw }

my %extensions =  %(
    '-my-align' => %(:synopsis("left | middle | right"), :default<middle>, :&coerce),
    '-my-misc' => %(), # can hold any value
);

my $module = CSS::Module::CSS3.module: :%extensions;
say $module.property-metadata<-my-align>.raku;
```

## See Also

- [CSS::Properties](https://css-raku.github.io/CSS-Properties-raku) - property-set manipulation module
- [CSS::Specification](https://css-raku.github.io/CSS-Specification-raku) - property definition syntax
- [CSS::Grammar](https://css-raku.github.io/CSS-Grammar-raku) - base grammars
- [CSS::Writer](https://css-raku.github.io/CSS-Writer-raku) - AST reserializer

## References

- CSS Snapshot 2010 - http://www.w3.org/TR/2011/NOTE-css-2010-20110512/
- CSS1 - http://www.w3.org/TR/2008/REC-CSS1-20080411/#css1-properties
- CSS21 - http://www.w3.org/TR/2011/REC-CSS2-20110607/propidx.html
- CSS3
  - CSS Color Module Level 3 - http://www.w3.org/TR/2011/REC-css3-color-20110607/
  - CSS Fonts Module Level 3 - http://www.w3.org/TR/2013/WD-css3-fonts-20130212/
  - CSS3 Namespaces Module - http://www.w3.org/TR/2011/REC-css3-namespace-20110929/
  - CSS3 Media Query Extensions - http://www.w3.org/TR/2012/REC-css3-mediaqueries-20120619/
  - CSS3 Module: Paged Media - http://www.w3.org/TR/2006/WD-css3-page-20061010/
  - CSS Selectors Module Level 3 - http://www.w3.org/TR/2011/REC-css3-selectors-20110929/
- SVG - https://www.w3.org/TR/SVG2/styling.html https://www.w3.org/TR/SVG/propidx.html

