Generate Shelf Edge Labels for printing mini labels (39mm × 39mm) and large labels (105mm × 73mm) onto special perforated paper. Each size has its own particular paper to space the labels correctly.
There are 2 supported "variants" of mini labels reflecting differing use case: stacked and shelf-emphasis. More details below.
The app provides three workflows for generating shelf edge labels:
- Specific Labels: Enter custom barcode values (one per line, comma-separated) in compact format (for example
01L01A,BR10L01A,BAK01A). Spaces and dashes are not accepted. Supports both mini large labels. - Aisle Labels: Generate sequential labels for store aisles, with configurable layout (mini or large SEL format) and optional shelf range selection (e.g., shelves B through D instead of always A through chosen value).
- Short Code Labels: Generate labels for back wall or front of store items, with custom prefix support.
All labels display:
- Aisle, side, bay, shelf values in several different variations as discussed next.
- A CODE128B barcode (always encoded compactly, without spaces or dashes, for reliable scanning)
- Encoded barcode value as readable text below the barcode for visual verification
Shelf values are always alphabetical (A-Z) across generated aisle and short code labels.
Special aisle values are defined in code via a config entry for values such as KIOSK or FLORAL. These only display that special aisle value, without side, bay or shelf.
Mini SEL supports two variations that share the same 39mm x 39mm geometry and barcode placement:
mini-three-row(default):- Row 1: aisle token or shortcode prefix
- Row 2: side + bay or bay (shortcode)
- Row 3: shelf token
mini-shelf-emphasis:- Row 1: enlarged shelf token
- Row 2: full spaced value (for example
01 L01 A)
Variant selection is available in-app via the Mini Variant control.
In all SEL types and sizes the barcode is at the bottom. Barcode payload encoding always uses compact values.
- Print: Render labels directly to your printer using browser print functionality, optimized for A4 SEL paper. You can 'print to PDF' if you want to download first.
flowchart TD
U[User Input] --> A[LabelApp + Tabs]
A --> MVU[Mini Variant Control]
A --> SF[SpecificLabelForm]
A --> AF[AisleLabelForm]
A --> BF[BackLabelForm]
A --> MPS[Service: miniVariantPreferenceStore]
MPS --> ST[localStorage miniVariant key]
A --> MVC[MiniVariantContext Provider]
MVU --> MPS
SF --> HUI[Hook: useFormValidationUi]
AF --> HUI
BF --> HUI
SF --> SVS[Service: specificLabelValidationService]
AF --> LGS[Service: labelGenerationService]
BF --> LGS
SF --> DI[Domain entry: src/domain/index]
AF --> DI
BF --> DI
SVS --> DI
LGS --> DI
DI --> CD[Domain: codesDomain]
DI --> GD[Domain: generationDomain]
DI --> CMP[Domain: compositionDomain]
SF --> LG[LabelGenerator]
AF --> LG
BF --> LG
LG --> UP[Hook: usePaginatedLabels]
LG --> UPP[Hook: usePrintPortal]
LG --> PL[Preview Path]
LG --> PR[Print Portal Path]
PL --> LT[LabelTile]
PR --> LT
LT --> MLT[MiniLabelTile]
LT --> LLT[LargeLabelTile]
MVC --> MLT
MLT --> CMP
CMP --> M3[mini-three-row compose/geometry/fit]
CMP --> MS[mini-shelf-emphasis compose/geometry/fit]
LLT --> LLC[LargeLabelTileContent]
LLT --> BBC[BarcodeBlock]
LLC --> DH[Display helper: getLargeSelDisplayParts]
LLT --> EB[Encoding helper: getEncodedLabelCode]
MLT --> BBC
The domain layer is intentionally consolidated into three modules:
src/domain/codesDomain.ts: Compact code parsing, display-part conversion, barcode encoding helpers, and specific-label validation.src/domain/generationDomain.ts: Aisle/short generation rules, numeric parsing, and form-input validation for generated label workflows.src/domain/compositionDomain.ts: Mini composition variants, geometry derivation, and typography fit logic used by tile rendering.
Use src/domain/index.ts as the public domain entrypoint for imports.
src/domain/*: Pure parsing, validation, generation, and formatting rules.src/services/*Service.ts: Application orchestration for label workflows (validation + generation).src/hooks/*: UI state and event orchestration for forms and preview/print behavior.src/components/*(render components): Presentation and layout rendering only.
Naming convention:
- Use noun-oriented module names for orchestration modules (
*Service.ts). - Use verb-oriented exports (
generateAisleLabels,generateShortLabels,validateSpecificLabels). npm run lint:namingenforces hook export/file naming as errors and reports boolean-prefix drift as warnings.- Naming lint runs as part of
npm run validate:ci.
Label layout is controlled by objects implementing ILabelLayoutStrategy. Each strategy declares two discriminants:
mode(LabelPrintMode):'mini-sel'or'large-sel'- the physical paper format.renderVariant(RenderVariant):'small'or'large'- controls large-vs-mini render.
Mini text arrangement is handled in src/domain/compositionDomain.ts.
Mini variant selection order:
- In-app Mini Variant selection (persisted to local storage).
- Fallback default:
mini-three-row.
To add a new mini variant:
- Add a new
MiniCompositionVariantIdliteral insrc/models/IMiniCompositionVariant.ts. - Implement
composeLabel,resolveGeometry, andfitTypographyinsrc/domain/miniCompositionVariants.ts. - Register the variant in the registry map in
src/domain/miniCompositionVariants.ts. - Add/update tests for
LabelTileand domain variant behavior.
All geometry values must remain in millimeters.
- Install dependencies:
npm install - Start the development server:
npm run dev
npm install also installs the repository's Git hooks, including a branch-aware pre-push hook:
- pushes to
mainrunnpm run validate:release - pushes to other branches run
npm run validate:ci
Generate a production-ready build with:
npm run build
The compiled output is written to dist.
To publish this app, deploy the contents of dist using your preferred static hosting provider or web server. Currently the app uses Github Pages.
Before publishing, validate the build locally if needed with:
npm run preview
This repository includes a GitHub Actions workflow that runs quality checks on pull requests to main and on pushes to main.
Quality checks run in CI:
npm run audit:prodnpm run styles:types:checknpm run styles:auditnpm run lintnpm run lint:namingnpm run lint:complexitynpm run test:runnpm run build
Run the fast local validation gate with:
npm run validate:ci
This runs:
npm run styles:types:checknpm run styles:auditnpm run lintnpm run lint:namingnpm run lint:complexitynpm run test:run(typecheck + unit tests)npm run build:bundle
The GitHub Pages deploy workflow uses a slightly stricter quality gate: the same checks as validate:ci, plus npm run audit:prod.
Run the full release validation gate with:
npm run validate:release
This runs:
npm run validate:cinpm run audit:prodnpm run test:a11ynpm run test:e2e
It fails on high or critical production vulnerabilities. These are the same branch-aware pre-push checks described above: pushes to main run npm run validate:release, and pushes to other branches run npm run validate:ci.
Deployment to GitHub Pages runs only after those checks pass, and for pushes to main. Workflow_dispatch can also be used to manually push a branch. The deploy job automatically retries once if GitHub Pages returns a transient post-upload failure.
- Push your latest changes to
main. - In GitHub, open Settings > Pages (already enabled).
- Set Source to GitHub Actions (already enabled).
- Wait for the
Deploy to GitHub Pagesworkflow to finish.
The site will be available at https://tonygorman.github.io/sel-generator/
Skill selection quick guide:
- Use
react-best-practicesfor React/render-path and print-geometry review. - Use
code-review-qualityfor testability, maintainability, and test-quality critique.
For code reviews in Copilot Chat, explicitly request the react-best-practices skill and include scope, validation commands, and expected output format.
For test-quality and maintainability reviews, explicitly request the code-review-quality skill with the same structure.
Use this template:
- Skill:
react-best-practices - Scope: files/folders to review
- Evidence: commands to run (
npm run validate:ciornpm run validate:release) - Output: findings first, ordered by severity, with file references
Example prompts:
Run a react-best-practices skills check on src/components and src/domain, then run npm run validate:ci. Return findings by severity with file links.Use react-best-practices for a full repo review and include release evidence from npm run validate:release.Skills check only for changed files in this branch using react-best-practices, with accessibility and print-path risks prioritized.
Shortcut prompt:
Do a react-best-practices skills check, full repo, include validate:release, findings first.Use code-review-quality to review tests in src/components and tests/e2e, include npm run validate:release, findings first with actionable fixes.
Generate typed CSS module declarations:
npm run styles:types
Check typed CSS module declarations are up to date:
npm run styles:types:check
Audit CSS/SCSS module classes for unused declarations and missing references:
npm run styles:audit
Run all unit tests:
npm run test:run
This now includes a TypeScript import/typecheck pass before Vitest runs.
Run the fast local validation gate:
npm run validate:ci
Run full release validation (adds dependency audit, accessibility, and E2E checks):
npm run validate:release
Run dependency audit only:
npm run audit:prod
Run accessibility checks (axe) against key views:
npm run test:a11y
This is required by the release validation gate (npm run validate:release) and must pass before release is considered complete.
This repo configures Git to use .githooks/pre-push, installed automatically by npm install via the prepare script.
The pre-push hook is branch-aware:
- Pushing to
main: runsnpm run validate:release(includes E2E). - Pushing to other branches: runs
npm run validate:ci(fast validation without E2E).
This ensures deploy-branch pushes have full confidence while keeping feature-branch iteration fast.
Run unit tests with coverage output:
npm run test:coverage
Run all Playwright E2E tests:
npm run test:e2e
If all E2E tests fail immediately at browser launch after dependency updates (for example missing Chromium executable), install browsers first:
npm run test:e2e:install
Run only one of the focused specs, e.g. the Aisle Labels flow:
npm run test:e2e -- tests/e2e/aisle-flow.spec.ts
The E2E suite is split into focused specs under tests/e2e/: tabs-accessibility.spec.ts, specific-flow.spec.ts, aisle-flow.spec.ts, back-flow.spec.ts, and visual-baselines.spec.ts.
Visual snapshots are part of the Playwright suite and are validated automatically when running npm run test:e2e.
tests/e2e/visual-baselines.spec.ts validates visual outputs for both label sizes:
- On-screen preview image snapshots for Mini SEL (35-label full page and shelf-emphasis aisle preview) and Large SEL (8-label full page)
If UI changes are intentional, update the snapshot baselines with:
npm run test:visual:update
Snapshot files are stored under:
tests/e2e/visual-baselines.spec.ts-snapshots
The barcode payload is always stored and encoded in compact format (no dashes or spaces), regardless of how users input or display the label code.
Specific Labels accepts compact input only (no spaces/dashes). Parsed valid inputs are encoded/scanned in compact form:
| Input Format | Barcode Payload | Barcode Output (Encoded/Scanned Value) | Display (Specific Labels) | Display (Aisle / Short code Labels) |
|---|---|---|---|---|
| Compact numeric aisle | 01L01A |
01L01A (always compact, no separators) |
01L01A |
01 L01 A |
| Compact prefixed aisle | BR10L01A |
BR10L01A (always compact, no separators) |
BR10L01A |
BR10 L01 A |
| Compact short code | BAK01A |
BAK01A (always compact, no separators) |
BAK01A |
BAK 01 A |
Display separators are presentational and do not affect barcode payload:
- Specific Labels accepts compact input only; secondary display stays compact.
- Aisle Labels and Short Code Labels generate codes programmatically; secondary display always uses spaces.
- Barcode in every case is always
01L01A-style compact payload.
Named aisle values are validated against the configured explicit allow-list (default: KIOSK, FLORAL, SEASONAL) rather than inferred from generic alphabetic input.
Configured compact prefixed aisle inputs are validated against the configured aisle-prefix allow-list (default: BR, BL, FL, FR, PD) and aisle numeric min/max bounds. These default prefixes represent store sections: BR = Back Right, BL = Back Left, FL = Front Left, FR = Front Right, PD = Produce.
Scanner reliability requires consistent, separator-free barcode payloads. The compact format ensures all scans decode to the same canonical form regardless of user input style.
The app supports two label sizes, selectable per print run.
- Paper: A4 landscape, 39mm × 39mm labels
- Layout: 7 columns × 5 rows (35 labels per page)
- Available on: Aisle Labels, Short code Labels, and Specific Labels tabs
- Paper: A4 portrait, 105mm × 73mm labels
- Layout: 2 columns × 4 rows (8 labels per page)
- Available on: Aisle Labels and Specific Labels tabs
- Select using the Mini SEL / Large SEL radio buttons
- Label content: mixed-size heading (aisle-side+bay-shelf) above a centred barcode
- Limitation: Special aisle values (e.g.
KIOSK,FLORAL) are not supported on large labels; use mini labels for special values. This is intentional to avoid complexity in large labels, in the absence of any actual user requirement.
Use this protocol whenever barcode sizing, typography, or print styles are changed.
Confirm generated labels remain machine-readable after:
- Browser preview
- Physical print
Create at least one sample sheet from each flow:
- Aisle flow: low, mid, high values (for example 01, 50, 99) and multiple side ranges
- Short code flow: bay range and shelf range coverage
- Specific flow: compact numeric aisle, compact prefixed aisle (for example BR10L01A), short code, and named aisle values
Include shelf coverage:
- alphabetical shelves only (
A-Z)
Run scans for each available combination:
- Printer type: thermal, laser, inkjet (as available)
- Scale: 100 percent only (no fit-to-page)
- Media: production label stock and plain office paper
Test with at least one scanner from each class available in store/ops:
- Fixed POS scanner
- Handheld laser scanner
- Handheld camera/imager scanner
For every printed sample:
- First-attempt scan rate should be 100 percent in normal operator use
- No manual keying required
- No repeated rescans for the same label under normal lighting
- Human-readable text must match the scanned value
If scan quality drops:
- Confirm print dialog used 100 percent scale
- Compare on-screen preview vs printed output
- Verify barcode module width and quiet-zone spacing were not reduced
Treat scan validation as a release gate for barcode-related changes. A change is not complete until:
- automated tests pass
- print-and-scan matrix pass is recorded by the validating owner