The spooky CSS in Perl toolkit
Tags: css, perl, outlook, mojolicious
Published: 2023-12-28 15:17:10

Some people are really going to love that: a spooky inline CSS  helper for composing Outlook messages in a Perl application!

Most websites those days are *SPA applications* that render on the front-side, or a mixture of backend and frontend rendering. There is also this trend of [CSS in JavaScript also knowns as JSS](https://cssinjs.org/) that is debatable for the simplest projects (makes everything overcomplicated), but in for more complex applications, can be justified and very useful.

Anyway, I use a lot of server side rendering. I mean, the old-school way you know, I serve HTML, CSS, and JavaScript separately.

A couple of years ago, I encountered one of those specific cases where it was useful to generate the CSS from some Perl code. We developed a crawler that helped us to interact with our in house project management API, so that we could send alerts to our colleagues when requirements had not been delivered and deadlines approached. Day minus 7, minus 3 and minus 1. And those colleagues used old versions of Outlook, so it was obligatory tables based emails, you know, those who have all kind of style and non-standard attributes all over the tags. It's actually not even standard CSS, it is Outlook specific style attributes in HTML:

```html
  <table
    align="center"
    bgcolor="#FFFFFF"
    cellpadding="0"
    cellspacing="0"
    class="w600"
    width="600"
  >
```
In such a situation, the tags and style are totally mixed. There is no separation of concerns any more.

There are tools (e.g. [Foundation Emails](https://get.foundation/emails.html)) for managing these kinds of beautiful things, but the integration with the crawler was more complicated than developing our own.

I thought of the *CSS in JS* thing and decided to implement a quick helper to inject the styled tags in appropriate places. Note that it starts to be interesting because when you compose such things, you have the same values repeated from dozens to hundreds of tags. And believe me, I had colleagues who really needed this automated, they would be asked to do it by hand otherwise. That's how things worked, in this company, e.g. there were no SCM tools and each *“programmer”* worked on their own repository, that were *“merged”* every few years. Also, production had hundreds of duplicates of the same app, but not the way you think of. Anyway, I'm digressing.

What I wanted was a nicely structured application, with separation of concerns, templates, no duplicated code and easy to maintain, although in the end it was for producing spooky inline CSS. This is how we would generate the previous HTML:

```perl
%= t table => ( class => 'w600', inline 'header-table' ) => begin
    ...
 % end
```
The basis of our toolkit is the `Mojolicious::Plugin::Email::Style::CSS` module that is strictly the same as the principle of *CSS in
JS* except it is *CSS in Perl* (I can hear some people crying, but this thing got designed with Outlook 2016 in mind, sorry).

It contains a list of constants that can be extended and nested.

```perl
package Mojolicious::Plugin::Email::Style::CSS;
use Mojo::Base -base, -signatures;

use constant PALETTE => {
  INFO    => '#4ECA47',
  WARNING => '#F0964B',
  DANGER  => '#F52930',
  WHITE   => '#FFFFFF',
};

use constant {
  
  ALIGNCENTER => {
    style => 'text-align: center'
  },
  CELLZERO     => {
    cellpadding => '0',
    cellspacing => '0',
  },

  HEADER_COLOR => [
    { bgcolor => PALETTE->{ INFO }},
    { bgcolor => PALETTE->{ WARNING }},
    { bgcolor => PALETTE->{ DANGER }},
   ],
  MAIN_WIDTH   => 600,
};
```

This is only a couple of [constant](https://perldoc.perl.org/constant) hashes that are structured as a [Sass file](https://sass-lang.com/) would be. I means: CSS with variables.

We also have a `css` subroutine that will take care of the *“preprocessing”*. It now looks even more like CSS.

```perl
sub css( $self ) {
 
  return {

    'align-center' => { ALIGNCENTER->%* },

    body => {
      leftmargin => '0',
      topmargin => '0',
      marginwidth => '0',
      marginheight => '0',
    },

    'top-table' => {
      bgcolor => PALETTE->{ WHITE },
      width => '100%',
      border => '0',
      CELLZERO->%*,
    },

    'content-table' => {
      align => 'center',
      bgcolor => PALETTE->{ WHITE },
      width => MAIN_WIDTH,
      CELLZERO->%*,
    },

    'header-table' => {
      border => '0',
      align => 'center',
      HEADER_COLOR->[ $self->alert_occurence - 1 ]->%*,
      width => MAIN_WIDTH,
      CELLZERO->%*,
      color => PALETTE->{ WHITE },
    },

    hello => {
      style => "color: #FFFFFF; " .
      "font-family: Arial, Gotham, 'Helvetica Neue', Helvetica,'sans-serif'; " .
      "font-size: 250%;"
    },

    'nbsp-top-margin' => {
      height => 17,
      style => "font-size: 17px; line-height: 17px"
    },
  }
}
```

The `CSS.pm` module also `has` an `alert_occurence` attribute so that we can send appropriate content and dynamically style the email depending on the emergency of the situation (day minus `7`, `3`, or `1`):

```perl
use Mojo::Base -base, -signatures;

has 'alert_occurence';
```

The `CSS.pm` module is registered as a `inline` helper in our `Mojolicious::Plugin::Email::Style` plugin, so that it can be consumed in [Mojo templates](https://docs.mojolicious.org/Mojo/Template):  

```perl
use Mojo::Base 'Mojolicious::Plugin', -signatures;
use Mojolicious::Plugin::Email::Style::CSS;

sub register ( $self, $app, $conf ) {
  my $style = Mojolicious::Plugin::Email::Style::CSS->new;
    $app->helper(
      inline => sub ( $self, $key ) {
        return $style->css->{ $key }->%*;
    });
}
```
The plugin is then loaded in a larger application:

```perl
package Email::Api;
use Mojo::Base 'Mojolicious', -signatures;

sub startup ( $app ) {
  # ...
  $app->plugin( 'Alerts::Api::Plugin::Style' );
  # ...
}

1;
```
# Templates

Here are the main templates used for the Outlook messages. This is the global layout:

```perl
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns:v="urn:schemas-microsoft-com:vml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0;" />
    <title>Alert</title>
    %= include 'layouts/style'
  </head>
  %= t body => ( inline 'body' ) => begin
    <%= content %>
  % end
</html>
```

And here is how we use our `inline` helper:

```perl
% layout 'outlook';
% title 'Alert';
%= t table => ( inline 'top-table' ) => begin
  %= t tbody => begin
    %= t tr => begin
      %= t td => begin
        <!-- content ~~~~~~~~~~~~~~~~~~~~~~~~~ -->
        %= t table => ( class => 'w600', inline 'content-table' ) => begin
          %= t tbody => begin
            <!-- header ~~~~~~~~~~~~~~~~~~~~~~~~~ -->
            %= t table => ( class => 'w600', inline 'header-table' ) => begin
              %= t tbody => begin
                %= t tr => begin
                  %= t td => ( align => 'center') => begin
                    <tbody>
                      <!-- top margin ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
                      <tr>
                        %= t td => ( inline 'nbsp-top-margin' ) => begin
                          &nbsp;
                        % end
                      </tr>
                      <!-- logo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
                      %= include 'fragment/logo'
                      <tr>
                        %= t td => ( align => 'center', inline 'hello' ) => begin
                          %# include 'fragment/hi'
                        % end
                      </tr>
                    </tbody>
                  % end
                % end
              % end
            % end
          % end
        % end
      % end
    % end
  % end
% end
%#= include 'fragment/alert/first'

```

# Conclusion

The examples for this blog post are very simple. Such old-style Outlook emails can use very-very verbose attributes and be duplicated all over the place, especially if you generate content. You obviously don't want that to be edited by hand. My idea with this project was to avoid that someone had to edit the emails by hand, because this is how things were managed usually in this company.

I thought about releasing that on the CPAN in the `Mojolicious::Pugin::*` namespace, but I had difficulties evaluating if it was a useful thing, really. 

Hope you liked that and that it was not too spooky.
 
# References

- [Initial thoughts](https://github.com/mojolicious/mojo/discussions/1653)
- [Foundation Emails](https://get.foundation/emails.html), a toolkit for generating emails from templates