ltn-print-labels is a standalone vanilla-JavaScript module (ESM, zero
runtime dependencies) that turns a class roster into a printable label
grid of student names — the kind you cut out and stick on trays, cubbies and
notebooks. It handles the fiddly parts a teacher actually hits: two children
called Léa become Léa M. / Léa C. (shortest last-name prefix that
separates the group), the class is repeated a whole number of times to fill an
A4 page, labels are butted together so the 1 px borders double as cut
lines, and every label gets its own font size — the nominal size for names
that fit, shrunk only for the ones that would overflow, so no column is ever
widened and no name is ever hyphenated.
I built it for Le Tableau Noir — where it powers the Étiquettes export — and released it as a reusable package.
Get the code
npm install ltn-print-labels
import { computeLabelLayout, createLabelPreview } from "ltn-print-labels";
import "ltn-print-labels/style.css"; // preview decoration only
const roster = [
{ firstname: "Ada", lastname: "Lovelace", level: "Grade 5" },
{ firstname: "Alan", lastname: "Turing", level: "Grade 5" },
];
// A ready-to-render model: cell grid + mm geometry, the same object the
// consumer POSTs to its PDF backend.
const layout = computeLabelLayout(roster, {
orient: "P", // "P" | "L"
cols: 4, // labels per row (1–7 portrait, 1–9 landscape)
fields: "first", // "first" | "last" | "both"
showLevel: true,
// fontMm omitted → auto: the largest size at which ~85 % of the labels fit
});
// Live WYSIWYG A4 preview, scaled to its container, no CSS transform.
const preview = createLabelPreview("#preview", roster, { orient: "P", cols: 4 });
preview.update(roster, { orient: "L", cols: 6 });
The module does not parse CSV — it takes plain { firstname, lastname, level? } objects, however you assembled them. computeLabelLayout also returns
fontMmAuto / fontMmMin / fontMmMax so a UI can drive the max-font slider,
and autoNominalFontMm(texts, labelWmm, ceilingMm) is exported on its own.
Live demo
This demo loads ltn-print-labels@0.1.15 — the current npm release — straight
from the jsDelivr CDN; the PDF export pulls
jsPDF the same way, only when you click.
Nothing is sent anywhere: the roster, the layout maths and the PDF are all
built in your browser. On the left, the input — the built-in Solvay 1927
roster (the 29 people in the 1927
Solvay Conference
photograph, with Solvay 1927 as the level) or your own CSV — plus every
layout option; on the right, the live A4 sheet the module renders, exactly
as it would print.
Input
Sample file: Solvay 1927.
Load a roster to see the label sheet here.