בס״ד

Introduction to Coding with SASS via Including ANSI Output in HTML

2018-10-25 16:45

This assumes you already have a firm grasp of CSS. A passing familiarity with SCSS may also be helpful.

Converting ANSI to HTML

While there are several utilities claiming the ability to convert ANSI to HTML, the only one I've found does the job correctly in all cases is ansi2html, a Python command line tool using IO redirection.

For example, the following outputs a list of the contents of the current directory in color, pipes it to ansi2html, and writes the result to output.html.

ls --color=always | ansi2html > output.html

This is usually all you need since the necessary CSS is included in the generated HTML. However I wanted to insert the pre portion of this HTML into markdown. Therefore I needed to generate all possible CSS myself.

Generating CSS for All Possible Styles with SCSS

The CSS resulting from the SCSS detailed below is all we need to make sure the output of ansi2html can be used directly and present correctly on the web.

Regular CSS

Due to text-based user interfaces (TUIs), terminals all default to a line height of one, so we do so as well.

.ansi2html-content {
  color: #CAC7BC;
  line-height: 1;
}

Terminals generally support basic formatting via Select Graphic Rendition (SGR). We do our best to translate this into CSS.

.ansi1 { font-weight: bold; }
.ansi2 { font-weight: lighter; }
.ansi3 { font-style: italic; }
.ansi4 { text-decoration: underline;; }
.ansi5 { text-decoration: blink; }
.ansi6 { text-decoration: blink; }
.ansi8 { visibility: hidden; }
.ansi9 { text-decoration: line-through; }

Variables and Lists

Variables in SCSS are names prepended with a dollar sign, assigned a value just like regular CSS properties.

In the following case we're assigning the variable $colors a list of hex color values.

Old terminals could only support 8 colors. While modern terminal emulators support 256 colors, they have 16 base colors in eight pairs of regular and bright.

$colors:
  #4A4948 #B35954 #799442 #B38C44 #5B8AB6 #AE5B84 #609794 #CAC7BC
  #030303 #D6655D #97BC4C #D9A74B #67A4DE #D3689C #75C0B9 #F3EFE1;

The set which I'm using above corresponds to the following.

                       

                       

Mixins and Interpolation

Mixins in SCSS are functions which generate SCSS. They're defined using the syntax below, beginning @mixin.

Interpolation in SCSS allows the use of variables in selectors and property names using the syntax #{}.

The color-classes mixin is taking a value $n and appending it to our class names and assigning those our $color value. It's used to build all of our text and background colors, as well as their inverses.

@mixin color-classes($n, $color) {
  .ansi3#{$n}, .inv4#{$n} { color: $color; }
  .ansi4#{$n}, .inv3#{$n} { background-color: $color; }
}

If you don't need to support inverse styles, removing those classes reduces the final CSS from 21.2KB to 15.6KB (when sent over the wire).

For Loops, Using Mixins, and Math

A for loop in SCSS iterates across a range. If using # to #, as below, not including the last number, while using # through # includes the last number.

Mixins are used via the @include syntax.

+ is used both as addition and string concatenation.

We generate color classes for our 16 base colors twice, once for the original codes used for 8 colors, and once for the codes used by 256 color terminals. This is done by looping through integers from 0 to 15 passing the correct code suffix and color to the color-classes mixin and including the generated SCSS.

@for $i from 0 to length($colors) {
  @include color-classes($i, nth($colors, $i + 1));
  @include color-classes("8-"+$i, nth($colors, $i + 1));
}

To generate all 256 colors, we need to generate 216 colors along the red, green, blue spectrum. This is done by looping through from 0 to 6 for each primary color carefully building the correct index and color.

@for $r from 0 to 6 {
  @for $g from 0 to 6 {
    @for $b from 0 to 6 {
      $index: 16 + ($r * 36) + ($g * 6) + $b;
      $color: rgb($r * 51, $g * 51, $b * 51);
      @include color-classes("8-"+$index, $color);
    }
  }
}

To generate the 24 grayscale colors of our 256 color palette we again loop carefully building the correct shade and index.

@for $i from 0 to 24 {
  $shade: ($i * 10) + 8;
  $grey: rgb($shade, $shade, $shade);
  @include color-classes("8-"+($i + 232), $grey);
}

A Glimpse of Deeper SCSS

There is more to programming with SCSS which I didn't happen to need for the above project.

If

In SCSS, if else is done two ways.

Via a built in function if(boolean_expression, if_true, if_false) which returns the result of evaluating the correct argument. The other argument is not evaluated at all.

Via an @if, @else if, @else set of directives for returning styles.

Map, Each, and While

In SCSS, a map is an ordered associative array with a set of functions for manipulation.

In addition to @for there are two other looping mechanisms, @while and @each.

Just as in most languages, the while loop is used @while boolean_expression {, looping until it is false.

Meanwhile, @each allows iteration over lists and maps (which is why we covered them). SCSS's @each also supports multiple assignment. For lists this means given a list of lists @each will give you the corresponding values from each sub-list each iteration. For maps this means you are given both the key and value each iteration.

Functions

Functions are very similar to mixins, except they return a value. They're defined using @function and use @return to return.