API

Slicing

AI assisted, human approved — novem uses AI to review and keep our documentation up to date.

The Novem slicing instruction is modelled on the python array slicing notation, but expanded with a few quality of life enhancements. The goal of slicing is to provide an easy way to subdivide your data for applying styles and formatting — and the SAME notation, with the same meaning, is used by every config file: table cells, chart colors, mark types, labels, marks, facets and legends.

In addition for the primary slicing function, we also allow the user to supply a comma separated list of indices starting at zero. (Negative indices count from the end in colon slices such as -1: and as a single bare index, but not inside a comma-separated list.)

Syntax

The novem slicing syntax is created to operate on sub-sections of tables. To achieve this we need two slices, one for rows and one for columns. These two slicers together is called a selector.

Any styles using selectors apply to the intersection of the two slices — the block of cells where the selected rows and columns cross.

Slicers

Note: Please be aware that slicing instructions cannot contain any spaces.

The slicing operation consists of one or more integers separated by a colon :. The first number denotes the starting position (counting from zero), the second the ending position and the third the step (how many items to skip). The integer values can be skipped to indicate start or end of a range.

0
Start index

Zero based index on where to start, if left empty the slice will start at index 0 (start of row/column).

A comma separated list of indices can be supplied instead, but then the remainder of the instruction must be removed.

:
Position separator
Splits up the start and end sections.
-1
End index

Which index to stop at. This can be an explicit value (5) or relative value from the end of the range (-5). If this element is not specified, the slice defaults to the last element of the range.

Note: The last element of a range is -1.

:
Step separator
Splits of the end and step sections.
2
Step value

This value indicates how many steps to take between each value, by default a step of 1 is assumed, selecting every element.

A step of 2 would thus select every other element, and so on. A negative step selects nothing — in every index space, the axis tick slot included; junk never broadens a selection.

Selectors

For a complete selector, a slice is specified twice, once for rows and once for columns. The instructions are separated with a space.

Below are a few examples of selectors along with some explanations.

-- select all rows and all columns
  :     :

-- select first row and all columns
 0      :

-- select last row and all columns
-1      :

-- select first three rows
 0:2    :   -- 2 is the third element (zero-indexed), and the stop is INCLUSIVE

-- select last three rows
-3:     :

-- select a 3x3 matrix in the top left corner of the table
 0:2   0:2
  :2    :2  -- the zero is implied if omitted

-- select every other row in the table
  ::2   :

-- select the first and last row
0,-1    :

-- select row 4,5,6
3,4,5   :   -- remember zero index

One index space: the raw file

Every selector addresses the raw uploaded file: (0,0) is the very first value of your csv. Row 0 is the header line, column 0 the leftmost column — on a table you can style them like any other cell.

Charts use the exact same coordinates. Column 0 is the x /category column and row 0 the header line; neither carries a paintable mark, so a selector that lands there simply paints nothing. In practice: the first series is column 1, the first data point row 1, and a line that works on a table means the same cells on a chart of the same csv. Pie and donut slices are rows — 2 : addresses the second slice.

Two spaces are deliberately not the csv, because they only exist after layout: facet strip-label cells and per-panel margins address the physical panel grid, and the axis grammar's third slot addresses positions in the generated tick sequence. They still use the same slicer vocabulary.

One slicer

A line with a single slicer addresses columns: revenue b bolds the revenue column, 1 red colors the first series. Two slicers are always rows × columns, and the two-slicer reading wins whenever both leading tokens parse as slicers.

Header names

A column slicer can use the column's name instead of its index: revenue, big_cat (spaces read as underscores, case-insensitive). Names work inside ranges and lists — revenue:costs, q1,q3 — and resolve to the column's raw position. Digits always win: a header literally named 2024 never hijacks the positional selector 2024. Row selectors are always positional.

Edge semantics

  • Stop is inclusive: 0:2 is three rows (the pandas .loc rule).
  • Overflows are inclusive: :1000 on 100 rows is all 100, -500: on 50 rows is all 50. Only a window entirely outside the axis — 100:200 on 5 rows, or a reversed range like 3:1 — selects nothing.
  • Negatives count from the end: -1 is the last row; an over-negative single index (-99 on 3 rows) selects nothing.
  • Junk selects nothing. A selector that fails to parse drops its line — it never degrades to "everything". In comma lists, invalid items drop individually and the rest survive.

Line markers: + and -

In the chart grammars (type, labels, marks, colors) and the table styling and format files, a + or - as the FIRST character of a line, followed by a space, is a line marker:

  • + <selector> [overrides] restates the nearest preceding line over a new selection — l:%v le:t then + 3 puts the same label on another series.
  • - <selector> erases those cells from every line above it, and later lines still paint into the hole.

Attached signs always belong to the slicer: -1 red colors the last column, - 1 erases column 1. Table styling and format files take the same markers: a bare - or + as the first token of a line is always a marker there too, and the historical tolerant reading of a bare leading - is retired. On a + line whose tokens are all slicer-shaped the tokens read as the new selector; when a slicer-shaped payload is meant (the align dash), write the row slicer explicitly (+ : 1 -).

Try it out!

Play around with a live demo of the slicer syntax:

Examples

Below are a couple of examples showing how the slicer selection can be used to apply different styles to a novem table.

Slicers and steps

This example shows a static color pattern reminiscent of a picnic blanket using three shades of blue and setting the background color.

We use four selectors and leverage the step instruction. First color alternating rows starting at different offsets, then add alternating columns. The selectors are applied in order, so later entries take precedence.

text/config/colors
-- Create a "picnic blanket" using 4 shades of blue
::2  :     bg blue-100  -- color every other row starting at 0
1::2 :     bg blue-200  -- color every other row starting at 1
1::2 1::2  bg blue-300  -- color every other col starting at row 1 col 1
::2  1::2  bg blue-200  -- color every other row starting at row 0 col 1

Index lists

Here we highlight a hierarchical structure in our table by coloring rows in descending shades of gray. This is a common strategy when wanting to preserve space.

Unlike in the picnic example above, here we explicitly choose which entries to highlight with a comma-separated list of rows.

text/config/colors
-- Color rows by "hierarchy"
1           : bg gray-500  -- world
2,5,8       : bg gray-400  -- region 1
3,6,9,12,19 : bg gray-300  -- region 2