Styling

Styles for the HTML output are set with CSS variables that override defaults set by a theme object. You can set your own styles by either defining your own theme object or overriding the default theme's styles with CSS variables. The latter approach is much more powerful and flexible. CSS should be used in place of the theme object whenever possible.

CSS Variables

The easiest and most scalable way to tune your animation's look and feel is to overwrite any or all of the available CSS variables. This enables you to tweak colors, animation speeds and basically everything else.

// No types :(
function add(a, b) {
  return a + b;
}
// Yes types!
function add(
  a: number,
  b: number
): number {
  return a + b;
}
// Yes types with generics!
function add<T extends number | bigint>(
  a: T,
  b: T
): T {
  return a + b;
}

See the CSS variable reference for a full list of things to tweak.

Theme objects

Theme objects are JavaScript/JSON objects that define the style defaults for the output, which are in turn overwritten by CSS variables. Theme objects are unwieldy monsters and less powerful than CSS variables. Their precise format also has a habit of changing in incompatible ways, so you should try to stick to CSS if at all possible.

The default theme object is available as an export from the main package, together with a Zod schema to validate theme objects with. TypeScript users can also import and use the Theme type.

import { defaultTheme, themeSchema, type Theme } from "@codemovie/code-movie";

let validatedTheme: Theme = themeSchema.parse(defaultTheme);
// Throws errors if the theme does not conform to the expected type
1234
import { defaultTheme, themeSchema, type Theme } from "@codemovie/code-movie";

let validatedTheme: Theme = themeSchema.parse(defaultTheme);
// Throws errors if the theme does not conform to the expected type

Styling hooks

Every animation has some pre-computed CSS variables, such as the number of rows, the total width, or the width of the line number colum. These are not meant to be changed, but rather to be read by your CSS. Information like the height of the entire animation or the width of the line number colum is useful if you want to add dimension-dependent borders, backgrounds, or decorative gimmicks (such as a fake editor window) to your animation.

See the styling hook reference for a full list of variables to hook into.

Example: window background

You can attach the pseudo elements ::before and ::after to the main animation element .cm-animation, which gives you two extra boxes to play with. As children of the element animation element they inherit all of its important CSS properties (such as the transition-duration), including the above styling hooks. The following example show how you can use ::before and the styling hooks to attach a background "window" to an animation that smoothly grows and shrinks to track the number of lines displayed in each keyframe:

.cm-animation::before {
  content: "";
  position: absolute;
  z-index: 0;
  top: calc(var(--vertical-align) * 50%);
  left: 0;
  height: var(--current-height);
  width: calc(var(--current-width) + var(--line-numbers-column-width));
  background: #fff;
  transform: translateY(calc(var(--vertical-align) * -50%));
  transition-property: all;
  transition-duration: inherit;
  border-radius: 1em;
  box-shadow: 0 0 2em #ccc;
}
123456789101112131415
.cm-animation::before {
  content: "";
  position: absolute;
  z-index: 0;
  top: calc(var(--vertical-align) * 50%);
  left: 0;
  height: var(--current-height);
  width: calc(var(--current-width) + var(--line-numbers-column-width));
  background: #fff;
  transform: translateY(calc(var(--vertical-align) * -50%));
  transition-property: all;
  transition-duration: inherit;
  border-radius: 1em;
  box-shadow: 0 0 2em #ccc;
}
// No types :(
function add(a, b) {
  return a + b;
}
// Yes types!
function add(
  a: number,
  b: number
): number {
  return a + b;
}
// Yes types with generics!
function add<T extends number | bigint>(
  a: T,
  b: T
): T {
  return a + b;
}

Typography

There are a few typography-related edge cases to consider when you venture past ASCII. If you don't intend to do that, you can ignore this section.

General rules

Code.Movie works on the assumption that you use a monospaced font for code. It creates a grid based on the width of the 0 character in your main font (set via font-family on the container element) and places every character and decoration on this grid. Anything but fixed-width fonts will lead to terrible-looking results.

CJK full-width characters

CJK full-width characters such as 한 occupy two grid columns.

Emoji

The first thing to note about emoji is that whether or not a character is rendered as an emoji (and how it gets rendered) depends entirely on the font being used. If you intend to use emoji I strongly recommend that you explicitly pick and use an emoji font like Noto Color Emoji - otherwise you are at the mercy of whatever your users have installed on their machines.

Emoji, like CJK full-width characters, are allocated precisely two columns of space. Whether this will look good depends on the combination of fonts being used. I recommend that you use the size-adjust CSS property in your @font-face rule to scale emoji to work well with your main font:

@font-face {
  font-family: "Noto Color Emoji";
  src: url("fonts/NotoColorEmoji-Regular.ttf");
  size-adjust: 80%; /* Works well together with Ubuntu Mono */
}
12345
@font-face {
  font-family: "Noto Color Emoji";
  src: url("fonts/NotoColorEmoji-Regular.ttf");
  size-adjust: 80%; /* Works well together with Ubuntu Mono */
}