100%

Canvas

Select Seated section in Drawing Tools, then click the canvas to place a section.

Getting started

Install dependencies and run the demo from the monorepo root:

pnpm install
pnpm dev

Import the editor in your Next.js or React app:

import {
  FloorPlanEditor,
  createEmptyFloorPlan,
} from "@ticketss/floor-plan-editor";
import "@ticketss/floor-plan-editor/styles/tokens.css";

Full install and publish steps — expanded in later milestones.

Package API

PropTypeDescription
valueFloorPlanDocumentControlled floor plan state
onChange(doc) => voidCalled when the document changes
isDarkModebooleanSets data-theme="dark" on editor root
langen | ar | pt | es | fa | frUI language (fallback en)
stylesFloorPlanEditorStylesOptional per-component chrome overrides (M11)
showSVGExportBtnbooleanWhen true, shows the Export SVG top-bar button and modal (default false)

Peer dependencies: react, react-dom

FloorPlanDocument

Single JSON blob for save/load. Top-level fields: schemaVersion, id, title, updatedAt, canvas, nodes, tiers, holds, optional selection.

import { createEmptyFloorPlan } from "@ticketss/floor-plan-editor";

const doc = createEmptyFloorPlan({ title: "Main Hall" });

See docs/floor-plan-json-schema-reference.md in the repo for the full schema.

Entity examples and validation — documented in schema reference.

Entities & commerce

EntityTierHold
TableWhole tableWhole table
G.A. AreaWhole zoneWhole zone
Seated section seatsPer seatPer seat
Image, Text, Shape

Drawing tools and property panels — M2–M9.

Design tokens

Chrome UI uses CSS variables from Ticketss main.scss, shipped as @ticketss/floor-plan-editor/styles/tokens.css. Toggle theme with isDarkMode on the editor.

Figma implementations must map colors to var(--*) tokens, not raw hex. See docs/design-tokens.md for the mapping checklist.

Full token table — in design-tokens.md.

Style customization

Pass styles to override chrome colors, backgrounds, and typography per component key. Canvas entities (tables, seats, shapes) stay in value.nodes — only editor chrome is customizable here.

Quick start

<FloorPlanEditor
  value={doc}
  onChange={setDoc}
  isDarkMode={false}
  lang="en"
  onSave={(saved) => {
    console.log(saved);
    alert("Floor plan saved");
  }}
  onCancel={() => alert("cancelled")}
  styles={{
    topBar: { backgroundColor: "#1a1a2e", color: "#eee" },
    topBarTitle: { fontFamily: "Georgia, serif", fontSize: 20, fontWeight: 700 },
    modeTabActive: { color: "#ff6b6b", fontWeight: 600 },
  }}
/>

Merge order

  1. Design tokenstokens.css via data-theme
  2. Package CSS — BEM classes in editor.css
  3. Inline overridesstyles[key] (highest priority)

Style keys reference

KeyDescription
rootEditor root wrapper — layout, background, typography baseline
topBarTop bar container
topBarTitleEditable floor plan title
topBarButtonUndo, redo, zoom, cancel, close icon buttons
topBarButtonPrimarySave button
leftRailLeft icon rail (desktop/tablet)
leftRailButtonPlus / drawing tools toggle
modeTabsMode tabs bar background
modeTabInactive mode tab
modeTabActiveActive mode tab
canvasCanvas viewport and stage area
canvasGridDot grid on canvas (merged with canvas element)
drawingToolsSidebarDrawing tools panel (left sidebar)
drawingToolsToolDrawing tool tile
drawingToolsToolActiveSelected drawing tool tile
propertiesSidebarEntity properties panel (right)
propertiesSidebarHeaderProperties sidebar title row
propertiesSidebarLabelProperty field labels
propertiesSidebarInputText inputs, textareas, custom selects
propertiesSidebarButtonSave, delete, duplicate actions in properties
tiersSidebarAssign pricing tiers panel
holdsSidebarSeat holds panel
editSeatSidebarSingle/multi seat edit panel

Override fields

Each key accepts: color, backgroundColor, fontFamily, fontSize, fontWeight, borderColor, plus any other valid CSS property as an escape hatch.

Examples

Rebrand top bar and active tab:

styles={{
  topBar: { backgroundColor: "#1a1a2e", color: "#eee" },
  modeTabActive: { color: "#ff6b6b", fontWeight: 600 },
}}

White-label drawing tools sidebar:

styles={{
  drawingToolsSidebar: { backgroundColor: "#fafafa" },
  drawingToolsToolActive: {
    backgroundColor: "#e8f4fc",
    color: "#0F66A9",
    fontWeight: 600,
  },
}}

Live playground

Select a component key in the tree below, edit override fields, and watch the editor above update immediately.

Component & class map

Select a styles key to edit overrides in the playground. CSS classes listed under each node are for reference or global stylesheets.

  • Outer wrapper (`floor-plan-editor`) — theme and direction

    • .floor-plan-editor
    • Chrome shell
      • .fpe-chrome
      • .fpe-body
      • .fpe-body__main
      • .fpe-body__workspace
      • Title, undo/redo, zoom, cancel, save

        • .fpe-top-bar
        • .fpe-top-bar__start
        • .fpe-top-bar__actions
        • .fpe-left-rail
      • Mobile action bar
        • .fpe-mobile-action-bar
        • .fpe-mobile-action-bar__plus
        • .fpe-mode-tabs-bar
      • Canvas area
        • .fpe-canvas-wrap
        • .fpe-sidebar
        • .fpe-sidebar--left
        • .fpe-drawing-tools
        • .fpe-sidebar
        • .fpe-sidebar--right
        • .fpe-sidebar
        • .fpe-tiers-panel
        • .fpe-sidebar
        • .fpe-holds-panel
        • .fpe-sidebar
        • .fpe-edit-seat-panel

Override: topBar

Top bar container

Live styles JSON

{}

Full reference: docs/component-styles.md in the repo. Package exports: EDITOR_STYLE_REGISTRY, FLOOR_PLAN_EDITOR_STYLE_KEYS, DEFAULT_EDITOR_STYLES.

SVG export

Export the floor plan as a hierarchical SVG with grouped seated sections, seats, tables, shapes, and sold overlay layers. Use the Export SVG button in the editor top bar, or call the API directly:

import {
  exportFloorPlanToSvg,
  downloadFloorPlanSvg,
  defaultIsSeatSold,
} from "@ticketss/floor-plan-editor";

const svg = exportFloorPlanToSvg(doc, {
  backgroundColor: "#F4F7FA",
  soldSeatOverlaySvg: '<circle r="10" stroke="red" fill="none"/>',
  allSoldOverlaySvg: '<rect width="100%" height="100%" fill="rgba(0,0,0,0.35)"/>',
  isSeatSold: (seatId) => defaultIsSeatSold(seatId),
});

downloadFloorPlanSvg(doc, { isSeatSold: defaultIsSeatSold });

Sold state is a placeholder in v1 (defaultIsSeatSold uses a stable hash per seat id). Pass isSeatSold from your host when real inventory is available.

Live JSON output

Current value from the editor above:

{
  "schemaVersion": 1,
  "id": "fp_demo",
  "title": "Floor plan editor",
  "updatedAt": "2026-01-01T00:00:00.000Z",
  "canvas": {
    "width": 4000,
    "height": 3000,
    "gridSize": 20,
    "zoom": 1,
    "viewport": {
      "x": 0,
      "y": 0
    }
  },
  "nodes": [],
  "tiers": [],
  "holds": [],
  "selection": []
}

Host integration

Controlled component pattern for your app state or future API:

const [doc, setDoc] = useState(() => createEmptyFloorPlan());

<FloorPlanEditor value={doc} onChange={setDoc} isDarkMode={dark} lang="en" />

// Future: await api.putFloorPlan(eventId, doc);

Top bar Save / Cancel: pass onSave (receives current FloorPlanDocument) and onCancel from your host app (e.g. persist to API or navigate away).