A Word-like horizontal ruler for web rich-text editors — left/right margins and first-line indent with draggable handles, keyboard control, and cm/in/px scales.
Every classic WYSIWYG editor (Froala, TinyMCE, CKEditor 5, Quill, …) ships without a ruler; only heavyweight document-model components (Syncfusion, DevExpress, ONLYOFFICE) have one. editor-ruler fills that gap with an editor-agnostic core plus thin per-editor adapters.
| Package | Description |
|---|---|
@devslab/editor-ruler |
Core: ruler UI, drag/keyboard handles, unit scales, guides, column markers. Zero dependencies, framework-free. |
@devslab/editor-ruler-froala |
Froala WYSIWYG editor plugin adapter. |
@devslab/editor-ruler-tiptap |
Tiptap extension (v2/v3). |
@devslab/editor-ruler-ckeditor5 |
CKEditor 5 plugin (ckeditor5 >= 42). |
npm install @devslab/editor-rulerimport { createRuler } from '@devslab/editor-ruler';
const ruler = createRuler(mountElement, {
unit: 'cm',
getMetrics: () => ({ contentWidth, leftMargin, rightMargin, firstLineIndent }),
onChange(change, phase) {
// apply px values to the current paragraph(s); phase === 'commit' on release
},
});
ruler.refresh(); // call whenever selection or content changesThe iife build exposes an EditorRuler global:
<script src="https://cdn.jsdelivr.net/npm/@devslab/editor-ruler@0.1/dist/index.global.js"></script>
<script>
const ruler = EditorRuler.createRuler(mountElement, { /* same options */ });
</script>The Froala adapter ships the same way — @devslab/editor-ruler-froala/dist/index.global.js exposes EditorRulerFroala.defineRulerPlugin (core bundled, single file).
Version pinning options:
| URL | Meaning |
|---|---|
@0.1.0 |
Exact version — never changes, cached longest |
@0.1 |
Latest 0.1.x patch — bugfixes auto-applied, no breaking changes (recommended) |
@latest (or no version) |
Always the newest release — majors included, so breaking changes can land without warning; jsDelivr caches the alias for up to 12h |
npm install @devslab/editor-ruler-froala froala-editorimport FroalaEditor from 'froala-editor';
import { defineRulerPlugin } from '@devslab/editor-ruler-froala';
defineRulerPlugin(FroalaEditor); // once, before creating instances
new FroalaEditor('#editor', {
rulerEnabled: true,
rulerUnit: 'cm',
toolbarButtons: ['bold', 'italic', '|', 'toggleRuler'], // optional toolbar toggle
});
// if you set pluginsEnabled explicitly, include 'ruler'npm install @devslab/editor-ruler-tiptap @tiptap/coreimport { Editor } from '@tiptap/core';
import StarterKit from '@tiptap/starter-kit';
import { EditorRuler } from '@devslab/editor-ruler-tiptap';
new Editor({ element, extensions: [StarterKit, EditorRuler], content });Indentation is stored as node attributes and rendered as plain inline CSS; a whole drag is one undo step. See the package README for options.
npm install @devslab/editor-ruler-ckeditor5 ckeditor5import { ClassicEditor, Essentials, Paragraph, Heading } from 'ckeditor5';
import { EditorRulerPlugin } from '@devslab/editor-ruler-ckeditor5';
ClassicEditor.create(element, {
licenseKey: 'GPL',
plugins: [Essentials, Paragraph, Heading, EditorRulerPlugin],
editorRuler: { unit: 'cm' },
});Model attributes down-cast to plain inline CSS; one undo step per drag. See the package README.
defineRulerPlugin registers the ruler plugin and toolbar commands — add what you need to toolbarButtons:
rulerOptions— recommended single button: one ruler-icon dropdown holding Show/Hide, Vertical Ruler, Lock Guides, Clear Guides, plus cm / inch / px (active states checkmarked)toggleRuler/rulerUnit— the same core functions as separate buttons, for hosts that prefer them split
Froala options: rulerVertical: true shows the vertical ruler on init; rulerGuides: false disables guide lines.
- Left margin / right margin / first-line indent handles (hanging indent supported)
- Vertical ruler (
createVRuler) — a scale strip for the editor's left edge - Guide lines (
createGuides) — design-tool convention: drag down from the horizontal ruler for a horizontal guide, right from the vertical ruler for a vertical guide (draggable, lockable, deleted by dropping back on the ruler; purely visual, never in the document HTML) - Live drag preview + a single undo boundary per gesture (
commitphase) - Keyboard accessible: handles are focusable sliders (
←/→,Shiftfor 10px,Home/End) - cm / in / px scales, switchable at runtime
- Themeable via CSS custom properties (
--edr-*), dark-mode aware - UI language follows the browser (
<html lang>→navigator.language; ko/en built in, overridable viadefineRulerPlugin(FE, { language, strings })andrulerLanguage) - Tab stops are out of scope for now — HTML has no native tab-stop model
- Tables & images push like Word: a selection in a table indents the whole table, paragraphs inside cells indent individually, and images move with their block
- Table column markers: inside a table the ruler shows draggable boundary markers that resize adjacent columns (skipped for tables with merged cells)
- Guide snapping: handles and column markers snap to nearby guides during drags (
guideSnapoption, default 5px) - Output is plain inline CSS —
<p style="margin-left: 75px; text-indent: 38px">— so exported HTML keeps its layout anywhere
Live playground (tabbed per-editor demos): https://devslab-kr.github.io/editor-ruler/
One-click sandboxes:
- Vanilla contenteditable on StackBlitz
- Froala on StackBlitz
- Tiptap on StackBlitz
- CKEditor 5 on StackBlitz
Locally:
pnpm install && pnpm build
# open demo/index.html in a browser (plain contenteditable + the core ruler)pnpm monorepo. pnpm build / pnpm test / pnpm typecheck run across packages. Versioning via changesets.