---
title: "Whiteboard styling"
description: "The CSS of the Tiptap Whiteboard board, and the data attributes and CSS variables that style shapes and the stage."
canonical_url: "https://tiptap-docs.lin52025i.qzz.io/editor/whiteboard/api-reference/styling"
---

# Whiteboard styling

The CSS of the Tiptap Whiteboard board, and the data attributes and CSS variables that style shapes and the stage.

The board adds only the CSS that it needs to work. Colors, fonts and cursors are up to your app, which styles the board with the data attributes on its elements.

## The board's CSS

`boardStyles` holds the CSS that the board needs to work: the size of the board and the area around it, how shapes hold their text, outline or component, where their handles and the cursors of other users show, and which parts of a frame take presses. The rules are in the `whiteboard` cascade layer, so your app's CSS outside of layers overrides them.

The board adds its CSS when its editor starts, so the `whiteboard` layer comes after the layers that your app declared before, like Tailwind's, and wins over them. To override the board from those layers, declare the `whiteboard` layer first:

```css
/* With Tailwind, before @import 'tailwindcss' */
@layer whiteboard, theme, base, components, utilities;
```

A doc frame is as tall as its document. Its `height: auto !important` also wins over `!important` rules of your app outside of layers.

The board adds this CSS to the page itself, in a `<style data-tiptap-style-whiteboard>` element. It uses the editor's `injectNonce`. An editor with `injectCSS: false` leaves the CSS to your app:

```js
import { boardStyles } from '@tiptap-pro/extension-whiteboard'

const style = document.createElement('style')
style.textContent = boardStyles
document.head.append(style)
```

Shapes have the size of their attributes. An app that sizes a shape by its content also sets `contain: none` on it.

## Zoom

Set `--zoom` to the zoom of the camera on the element that holds the editor. The handles and the cursors of other users read it to keep their size at every zoom. Divide your own outlines and borders by it too:

```css
[data-shape][data-selected] {
  outline: calc(2px / var(--zoom, 1)) solid #6a00f5;
  outline-offset: calc(2px / var(--zoom, 1));
}
```

## Shapes

Every shape element has `data-shape`, and a `data-type` with the kind of shape: `sticky-note`, `geo`, `free-text`, `freehand`, `doc-frame`, `frame`, `board-image` or `arrow`. [Custom shapes](https://tiptap-docs.lin52025i.qzz.io/editor/whiteboard/guides/custom-shapes.md) have the `data-type` from their `shapeNodeView`. The board element has `data-type="board"`.

| Attribute              | On                             | Description                                                                                |
| ---------------------- | ------------------------------ | ------------------------------------------------------------------------------------------ |
| `data-id`              | Every shape                    | The id of the shape                                                                        |
| `data-color`           | Shapes with a color            | The name of its color, like `blue`. Custom shapes have it with a `color` attribute         |
| `data-font-size`       | Shapes with text               | `s`, `m`, `l`, `xl` or a name of your own                                                  |
| `data-text-align`      | Shapes with text               | `start`, `center` or `end`                                                                 |
| `data-font`            | Shapes with text               | `sans`, `serif`, `mono` or a name of your own                                              |
| `data-kind`            | Geometric shapes               | `rectangle`, `ellipse`, `diamond` or `triangle`                                            |
| `data-selected`        | Selected shapes                | Present while the shape is selected                                                        |
| `data-editing`         | The edited shape               | Present while its text, or the document of a doc frame, is edited                          |
| `data-remote-selected` | Shapes that other users select | Present while another user selects the shape. That user's color is in `--remote-selection` |
| `data-erasing`         | Every shape                    | Present while the eraser is about to erase the shape                                       |
| `data-arrow-target`    | Shapes that arrows connect     | Present while a dragged arrow end would connect the shape                                  |

The `shapeElementAttributes` option of `WhiteboardKit` adds attributes of your own, like the `data-suggestion-*` attributes of [Tracked Changes](https://tiptap-docs.lin52025i.qzz.io/editor/whiteboard/guides/tracked-changes.md).

### Parts of shapes

| Selector                       | Description                                                                                                                                               |
| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[data-shape-content]`         | The element that holds the text of a shape, or the document of a doc frame                                                                                |
| `[data-shape-outline]`         | The SVG outline behind the text of a geometric shape. It stretches over the whole shape                                                                   |
| `[data-shape-handle='resize']` | A resize handle on a corner of a selected shape. `data-corner` is `top-left`, `top-right`, `bottom-left` or `bottom-right`                                |
| `[data-arrow-handle]`          | A handle on an end of a selected arrow. Its value is `from` or `to`                                                                                       |
| `[data-arrow-label]`           | The label of an arrow, in the middle of its line. Without text, it has `data-empty` and shows only while it is edited                                     |
| `[data-shape-component]`       | The element of a custom shape's node view. It has the size of the shape, and so does a `NodeViewWrapper` in it                                            |
| `[data-shape-interactive]`     | Your mark on parts of a custom shape's component that take their own presses, like the legend of a chart                                                  |
| `[data-frame-title]`           | The name of a frame, above its top left corner and as wide as the frame at most. A press on it selects and moves the frame, and a double click renames it |
| `[data-frame-title-field]`     | The field in the name of a frame while the frame is renamed                                                                                               |
| `[data-frame-edge]`            | An SVG over a frame, whose edge takes the presses on the frame's edge. The rest of the frame lets presses through to the board                            |

A shape in a frame has an inline `clip-path`, which cuts it off at the edges of the frame while it is not selected.

An image shape holds an `img`, which fills the whole shape. For a shape with other proportions than its image, like one that `insertShape` adds with a size of your own, give the `img` an `object-fit`:

```css
[data-type='board-image'] img {
  object-fit: cover;
}
```

Arrows and pen lines are drawn in the current color, so set their `color`. The outline of a geometric shape is a path that your CSS fills and strokes:

```css
[data-type='arrow'],
[data-type='freehand'] {
  color: var(--stroke);
}

[data-shape-outline] path {
  fill: var(--fill);
  stroke: var(--stroke);
  stroke-width: 2px;
  vector-effect: non-scaling-stroke;
}
```

## Colors

Shapes, text colors and highlights store the name of a board color, so your CSS decides how each one looks. One way is a few variables for each color:

```css
[data-color='blue'] {
  --stroke: #0d74ce;
  --fill: #ebf5ff;
  --note: #bfe0ff;
}

[data-type='sticky-note'] {
  background: var(--note);
}
```

Text colors are `span` elements with `data-color`, and highlights `mark` elements with `data-color`. See the [example](https://tiptap-docs.lin52025i.qzz.io/editor/whiteboard/examples/simple.md) for the styles of all colors.

To add a color of your own, add its name to the `colors` option of `WhiteboardKit`, and style it like the others:

```js
import { shapeColors, WhiteboardKit } from '@tiptap-pro/extension-whiteboard'

WhiteboardKit.configure({ colors: [...shapeColors, 'brand'] })
```

Commands take any name. The list decides which text colors and highlights pasted HTML keeps: other text colors go away, and other highlights become yellow.

## Stage

`attachStage` marks the stage, so your CSS can show the tool and the pan:

| Attribute   | Description                                                    |
| ----------- | -------------------------------------------------------------- |
| `data-tool` | The active tool, like `select` or `pen`                        |
| `data-pan`  | `ready` while space is held, and `active` while the board pans |

While a drag runs, the stage holds a preview element:

| Selector               | Description                                                                                                                   |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `[data-selection-box]` | The box of a selection drag                                                                                                   |
| `[data-shape-preview]` | The box of a shape while a tool draws it. Its value is the tool, like `note`, and `data-color` holds the chosen color, if any |
| `[data-arrow-preview]` | The line of an arrow while the arrow tool draws it, in the current color                                                      |
| `[data-pen-preview]`   | The line of the pen while it draws, in the current color. `data-color` holds the color of the line                            |

The previews are not scaled with the board, so they need no `--zoom`:

```css
[data-selection-box],
[data-shape-preview] {
  background: rgba(106, 0, 245, 0.1);
  border: 1px dashed #6a00f5;
}

[data-arrow-preview] {
  color: #6a00f5;
}
```

## Cursors of other users

[`createRemoteCursors`](https://tiptap-docs.lin52025i.qzz.io/editor/whiteboard/api-reference/utilities.md#createremotecursors) puts the cursors in a `data-remote-cursors` element next to the editor's element, over the shapes:

| Selector                     | Description                                                                                                                                                |
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[data-remote-cursor]`       | A cursor, with its user's color in `--remote-cursor`. It has `data-idle` after a minute without moving, and `data-leaving` while it goes. Both fade it out |
| `[data-remote-cursor-arrow]` | The SVG arrow, with its tip where the user points                                                                                                          |
| `[data-remote-cursor-name]`  | The user's name, next to the arrow. It doesn't show without a name                                                                                         |

An app that sets `--zoom` only once zooming pauses can set it at once on the `data-remote-cursors` element, so the cursors keep their size while the board zooms.

## Keyboard

While keys act on the board, the editor element has `data-keyboard="board"`. While the text of a shape is edited, it doesn't. It stays while the document of a doc frame is edited.

## Read-only

While the board is [read-only](https://tiptap-docs.lin52025i.qzz.io/editor/whiteboard/guides/shapes-and-tools.md#read-only-boards), the editor element has `data-read-only`, and selected shapes show no handles. Use it for cursors that promise a change, like a move cursor on shapes:

```css
.tiptap[data-read-only] [data-shape] {
  cursor: default;
}
```
