Quick Guide: Customizing your canvas (rotating nodes, removing borders/backgrounds)

While canvas is already a very useful tool and an excellent addition to Obsidian, it’s still missing some basic customization options. Until these features are added, this guide will provide some basic CSS to extend these options. Simply add any of these code examples into a new CSS Snippet.

Targeting specific canvas elements

To ensure our modifications don’t apply to every single element on our canvas, we need to add a custom CSS class to the YAML header of the note we have included in your canvas (it doesn’t have to be called canvasimage). This class can be used in as many notes as we want.

---
cssclass: canvasimage
---

We can then target any node in our canvas using the CSS :has() selector like so:

.canvas-node:has(.canvasimage) {
    // add CSS styling here
}

Customising Images

By default, Obsidian has the Show inline title option enabled. We can’t style images if we add them directly into the canvas, so we have to embed images into a note, and then add that note to the canvas. Disable this option so the note title doesn’t show above our image.

!400|

Removing a node border

.canvas-node-container:has(.canvasimage) {
    border: none;
}

Removing a node background

.canvas-node-container:has(.canvasimage) {
    background: none;
    box-shadow: none;
}

Removing a node label

.canvas-node:has(.canvasimage) .canvas-node-label {
    display: none;
}

Rotating a node

.canvas-node:has(.canvasimage) {
    transform:rotate(20deg);
}

20deg can be whatever value (in degrees) we want, including negative numbers

Removing the default rounded node corners

.canvas-node-container:has(.canvasimage) {
    border-radius: 0;
}

Removing the default padding between the image and the node border

.markdown-preview-view.canvasimage {
    padding: 0px !important;
    overflow: visible;
}
.markdown-preview-view.canvasimage::before {
    display: none !important;
}
.markdown-preview-view.canvasimage p {
    margin: 0 !important;
}

Additional CSS for rotating text

Because of some rendering optimization weirdness, we need to add the following CSS to any note with text, or the text will get blurry and difficult to read when it’s rotated:

.canvas-node-container:has(.canvastext) {
    overflow: visible;
    contain: none;
}
.canvas-node-content:has(.canvastext) { 
    will-change: transform;
}

Adding an image background to our canvas

This is a bit hacky, but it works reasonably well in most cases - what we’re doing is creating a pseudo-element of our canvas element, and expanding it to be much bigger than our viewport. It’s not infinite, but we can expand it as much as we want.

.canvas {
    position: relative;
}
.canvas::before {
    --image-scale: 25%; 
    --background-scale: 500%;
    content: "";
    width: var(--background-scale);
    height: var(--background-scale);
    background: url("https://i.imgur.com/DtGQ9Em.jpg");
    position: absolute;
    top: calc(-1 * var(--background-scale)/2 + 50%);
    left: calc(-1 * var(--background-scale)/2 + 50%);
    background-size: var(--image-scale);
}

In this example, we change the background to a tiling leather texture. To replace this, we can simply replace the url in the background property and fiddle with the --image-scale and --background-scale properties until it looks how we want

NOTE: This background will appear on all canvases. If we want it to only appear on a specific canvas, we need to use the :has() selector on a node that only appears in that canvas - eg:

.canvas:has(.canvasimage) {
  position: relative;
}

For some inspiration on what sort of things are currently possible to achieve using snippets, check out my DnD Character Sheet Design (made using Canvas & Snippets)

28 Likes

Thanks for the snippets.

Using the above snippet to remove all borders, the borders were gone, but there was still a little dropshadow (which looks like a thin border). I’m using the default Obsidian theme.

Using the below snippet, I was able to remove both the border and the dropshadow:

.canvas-node-container {
    border: none;
}
.canvas-wrapper {
  --shadow-stationary: none;
}

I’m not using any special class here (but you could add it if you need it), I just remove all borders from all notes and cards. Using the “MySnippets” plugin to quickly toggle the snippet. Works for me.


Update: with the following code, the borders will disappear, but the border will reappear for a selected card – which is useful for resizing, connecting the card to other cards,…

.canvas-node-container {
    border-width: 0;
}
.is-focused.canvas-node .canvas-node-container {
    border-width: 2px;
}
.canvas-wrapper {
  --shadow-stationary: none;
}
4 Likes

Here is a snippet to center the text for all cards except for the embedded notes.

Useful when hiding all borders (see my snippet above), since the connecting lines are sometimes a bit off (if you use connecting lines).

.canvas-node-container {
    text-align: center;
}
.canvas-node-container .is-loaded {
  text-align: left;
}
3 Likes

You’re right - the border removal class should include the line

    box-shadow: none;

Looks like I accidentally included it in the background removal class.

Anyone know a snippet that would change just the ends of the arrows between cards? Not the path itself but just the triangle at the end.

Thanks for the snippet. I’m trying to vertically align text in canvas container but with no luck. Any suggestions? Thanks.

How can I make it only on the first line?
Doesnt work:
.canvas-node-container::first-line { text-align: center; }

@Gregor - text-align: center doesn’t work on first-line in css, it only works on a whole block level element (paragraph or heading).

To center only the first title/heading or paragraph of a card, you can try this:

.canvas-node-container div:nth-child(2) {
    text-align: center;
}

Example:

Note the use of double line breaks to separate text in separate paragraphs.

(This code may stop working if Obsidian changes the code structure of canvas elements.)

1 Like

thank you!
samueldee vs ChatGPT
1 : 0

How to change the color of connection line between nodes in canvas?

Anybody know of a way to set the canvas background image to an image that’s inside your vault? I’ve tried multiple CSS settings for background: url but nothing works, e.g. not working

background: url("file:///Users/..../my_image.png");

Hello!
I followed the advice above, and everything works perfectly. Thank you very much.

But there is one problem - I can’t seem to remove the faint border around the image. I tried making the border the same color as the background, tried adding a shadow in the background color, and some other things, but nothing helps.

Maybe someone knows how to get rid of this subtle pale border?

It’s driving me crazy :slight_smile:

Hello There, I was missing the ability to add a title to my cards (just as a group).
So I made this css (it was hard to allow the title to appear outside the note so maybe some experts could find a better code)

/* Style for our ".card-title" class */
.canvas-node-container .card-title {
    position: absolute;
    left: 0;
    top: calc(-1 * var(--size-4-1) * var(--zoom-multiplier));
    transform: translate(0, -100%) scale(var(--zoom-multiplier)); 
    transform-origin: bottom left;
    overflow: hidden;
	/* max-width: calc(100% / var(--zoom-multiplier)); */
    text-overflow: ellipsis;
    white-space: nowrap;
    pointer-events: initial;
    font-size: 1.3em;
    padding: var(--size-4-1) var(--size-4-2);
    border-radius: var(--radius-s);
    color: var(--text-muted);
    background-color: rgba(var(--canvas-color), 0.1);
    line-height: 1;
}

/* Make sure the card content allows the title to overflow */
.canvas-node-content, .canvas-node-container, .markdown-preview-view {
    overflow: visible;
}
.canvas-node-container {
	contain: size;
}

Here is an updated version that is not modifying the overflow (instead is drawing the border below the title)

/* Drawing the title */
.canvas-node-container .card-title {
    white-space: nowrap;
    pointer-events: initial;
    font-size: calc(1.3em * var(--zoom-multiplier));
	position: relative;
    padding: var(--size-4-1) var(--size-4-2);
	display: inline-block;
	margin-left: calc(-1 * var(--size-4-6));
	top: calc(-1 * var(--font-text-size));
    margin-bottom: -100px;
    border-radius: var(--radius-s);
    color: var(--text-muted);
    background-color: rgba(var(--canvas-color), 0.1);
    line-height: 1;
}

/* Removing the border */
.canvas-node-container:has(.card-title) {
	border: none;
    box-shadow: none;
	background-color: transparent;
}

/* Drawing a new border below the title */
.canvas-node-container:has(.card-title)::before {
    content: "";
    position: absolute;
    top: calc((var(--font-text-size) * var(--zoom-multiplier) * 1.3) + (2 * var(--size-4-1) * var(--zoom-multiplier)) + 10px);
    left: 0;
    right: 0;
    bottom: 0;
    border: 2px solid rgba(var(--canvas-color), 1);
    border-radius: var(--radius-m);
    pointer-events: none;
	background-color: var(--background-primary);
}
1 Like