NABI NOTE
Docs

Custom styles

The default stylesheet injects itself on mount. You import nothing and the editor still comes up looking like itself.

There are two ways in. For colors and shapes you only override variables; when that is not enough, you take the stylesheet.


Colors and shapes are variables

Component rules contain not a single color literal. Everything is drawn through --nabi-* variables, so overriding them carries the rest along.

css
.nabi,
.nabi-overlay {
  --nabi-accent: #7c3aed;
  --nabi-radius: 4px;
  --nabi-min-height: 400px;
}

Do not forget .nabi-overlay

Floating boxes — the @ menu, the table size grid, address prompts, notices — attach to document.body. They are not inside the editor, so overriding .nabi alone leaves those boxes on the default colors.

TokenWhatDefault
--nabi-font · --nabi-font-monotypefacessystem fonts
--nabi-font-size · --nabi-line-heightbody size · leading1rem · 1.7
--nabi-bg · --nabi-bg-subtle · --nabi-bg-hoversurfacesnear-white
--nabi-color · --nabi-muted · --nabi-on-accenttextnear-black · grey · white
--nabi-border · --nabi-accentlines · accent#d8dce3 · #3b6ef5
--nabi-danger · --nabi-on-dangerdestructive actionsreds
--nabi-radius · --nabi-radius-smcorners12px · 8px
--nabi-padding · --nabi-min-heightbody padding · min height16px 18px · 240px
--nabi-shadow · --nabi-scrimbox shadow · lightbox backdrop
--nabi-dropcap-lineslines a drop cap spans3
--nabi-code-*code token colors (keyword, string, …)GitHub-ish
--nabi-z · --nabi-z-lightbox · --nabi-z-fullscreenstacking ordervery large

Staying clear of specificity

The theme rule is :where(html, body).dark .nabi, which is specificity (0,2,0). Match that number and the result depends on which stylesheet loads first. Repeat a class to lift yours out of load-order territory.

css
/* What this site does — three classes take it to (0,3,0). */
.nabi.nabi.nabi,
.nabi-overlay.nabi-overlay.nabi-overlay {
  --nabi-accent: var(--my-accent);
}

Light and dark

A dark class on either html or body means dark; light means light. With neither, light is the default; with both, the explicit light wins.

html
<html class="dark"><!-- or <body class="dark"> --></html>

There is nothing to tell the editor and no API to call — toggle the class and the CSS reacts. The editing area and the floating boxes change together, and color-scheme follows so even browser chrome like scrollbars matches.

A theme swaps color variables only. Not one component rule is rewritten, so styles you write yourself get dark mode for free as long as they use --nabi-* variables.


Taking the stylesheet

You can turn the injection off and handle the file yourself. The same content also ships as dist/nabi.cssthe core sheet and the bundled wings' sheets concatenated, so that one file gives you everything.

ts
import 'nabi-note/style.css' // through a bundler, or

nabi.create('#editor', { injectStyles: false }) // link it yourself and turn injection off

You can also take the sheet as a string and place it yourself.

ts
import { NABI_CSS, NABI_STYLE_ID } from 'nabi-note'
// NABI_CSS is the core sheet; NABI_STYLE_ID is the id of the injected <style>.

Wings carry their own sheets

A registered wing's styles is injected after the core sheet. Identical content goes in once per document, so several editors on a page never stack sheets. That is why, if you turn injection off and only link a file, you want dist/nabi.cssNABI_CSS holds the core alone, without the wing rules.


What you can target

For anything variables cannot reach, aim at the classes. These names are a public surface.

SelectorWhat
.nabithe shell around the whole editor
.nabi__toolbar · .nabi__group · .nabi__buttonthe toolbar and its button groups
.nabi__commands · .nabi__controlthe context row and controls a wing drew itself
.nabi-notethe document root — the one class you add when rendering stored HTML
.nabi-editorthe editing area (editor-only rules: caret, selection marks, placeholder)
.nabi-overlaythe shared shell of every floating box
.nabi-menu · .nabi-gridthe @ menu · the table size grid

State shows up as classes on the shell — .nabi--readonly, .nabi--locked (upload running), .nabi--dropping (a file is being dragged in), .nabi--fullscreen.

Screen-only marks are targetable too — data-nabi-selected (an object picked whole), data-nabi-active (inside an object you edit), data-nabi-object (caret hidden), data-nabi-empty (empty document), data-nabi-cell-selected (table cells picked by drag or keyboard), data-nabi-cell-anchor (that selection's anchor), data-nabi-cell-dragging (a table being dragged across). None of these seven leave in the saved value.


Rendering stored HTML elsewhere

Outgoing values freeze only the looks that carry meaning. The rest — heading sizes, table borders, drop caps, checklist boxes — belongs to the sheet, so rendering it the same way takes two things.

html
<link rel="stylesheet" href="/nabi.css" />
<div class="nabi-note">your stored HTML</div>

.nabi-note carries document formatting only — none of the editing-screen rules (caret, selection outlines, placeholder) can reach it, so what you render is exactly what the preview shows.

To keep viewer-side behavior — click-to-enlarge on images, sortable tables — attach nabiViewer. It ships as its own entry point, nabi-note/viewer, so the viewing page does not carry the whole editor.

ts
import { nabiViewer, image, table } from 'nabi-note/viewer';

const detach = nabiViewer(document.querySelector('#article')!, {
  wings: [image(), table()],
});

It is the same syntax as picking wings for the editor — only what you register runs, and the detach function undoes everything that was attached. Styles are injected automatically, so it looks right even on a page with no editor. One rule: never attach it to an element you edit and save back — the viewer inserts buttons and reorders rows, and saving that DOM leaks them into the value. The viewing side must be a read-only copy. (imageViewer(root) is the old single-feature name; it keeps working through 0.1.x as an alias and is removed in 0.2.0.)

Renamed in 0.1

Earlier versions said to use class="nabi__content" here. That name still works throughout 0.1.x as an alias, and will be removed in 0.2.0 — switch to .nabi-note.


Next