How do we add backoffice styles to Tiptap? Adding the details native extension for Tiptap

I am trying to activate the “DETAILS” native Tiptap extension.

So following this guide Extensions | Umbraco CMS is a good start, and it brought me to the point where we have a toggle for details in the toolbar that actually works.

However, it looks a bit wonky:

Obviously the button needs some styling, and there’s a lot more to improve.

At this point I’m unsure if Tiptap should have “brought” any styles of it own to make this look better but I assume this is expected and thus I move to the next step to solve it.

This is the main typescript file of my extension. The equivalent from the tutorial here is the file highlight.tiptap-api.js :

import { UmbTiptapExtensionApiBase } from "@umbraco-cms/backoffice/tiptap";

import { Details } from "@tiptap/extension-details";

import { DetailsContent } from "@tiptap/extension-details-content";

import { DetailsSummary } from "@tiptap/extension-details-summary";

import { Highlight } from '@tiptap/extension-highlight';

import './styles.css';



export default class UmbTiptapDetailsExtensionApi extends UmbTiptapExtensionApiBase {

getTiptapExtensions = () => [Highlight, Details.configure({

persist: true

  }), DetailsContent, DetailsSummary];

}


Note the styles import. This is from what I gather the proper way to introduce styles.

However, I can’t figure out how to get the styles imported in the backoffice.

No matter what styles I apply, I can’t see any effect of them and I can’t see any css file regarding this component being loaded.

Does anyone here know how to include css the import way like above and actually have the backoffice load it?

The vite build results in the css file in my App_Plugins folder, and I’ve also tried a variant where I make sure that css file has a hash in the filename like the other generated js files for example. It is never loaded however.

I can only get the h1, h2 etc. to use the stylesheet in the backoffice. The stylesheet has the following syntax.

/* Scoped to the editor */
.tiptap h1 {
  font-size: 3rem;
  color: red;
}
.tiptap h2 {
  font-size: 2rem;
  color: pink;
}
.tiptap h3 {
  font-size: 1.8rem;
  color: green;
}
.tiptap p.lead{
  font-size: 3rem;
}

I cannot get the paragraph lead style to work, so need to find out a way I can add custom styles that can be selected in the RTE by editors etc. Not sure if this will help. :thinking:

1 Like

How do you introduce that CSS file into the backoffice? Also via javascript import? Or some other way?

Hi Karl, this was added directly in the Umbraco CMS backoffice under stylesheets.

And then referenced in the data type settings:

Well we ended up solving it with this AI-generated abomination.

I sure hope there’s some other cleaner way to solve this, but this appeared to have worked for now:

import { Extension } from '@tiptap/core';
import { Details } from "@tiptap/extension-details";
import { DetailsContent } from "@tiptap/extension-details-content";
import { DetailsSummary } from "@tiptap/extension-details-summary";
import { UmbTiptapExtensionApiBase } from "@umbraco-cms/backoffice/tiptap";
import styles from './styles.css?inline';

// Helper function to inject styles
function injectStylesIntoShadowDOM(editorElement: HTMLElement) {
  const styleId = 'tiptap-details-styles';
  
  if (!editorElement) return;

  // Find the shadow root
  const root: Document | ShadowRoot = editorElement.getRootNode() as Document | ShadowRoot;
  
  // If we're in a shadow DOM and haven't injected yet
  if (root instanceof ShadowRoot) {
    if (!root.querySelector(`#${styleId}`)) {
      const styleElement = document.createElement('style');
      styleElement.id = styleId;
      styleElement.textContent = styles;
      root.appendChild(styleElement);
    }
  } else {
    // Fallback to document head
    if (!document.getElementById(styleId)) {
      const styleElement = document.createElement('style');
      styleElement.id = styleId;
      styleElement.textContent = styles;
      document.head.appendChild(styleElement);
    }
  }
}

// Create a custom extension to inject styles into the shadow DOM
const StyleInjector = Extension.create({
  name: 'styleInjector',

  onCreate() {
    const editorElement = this.editor.view.dom;
    injectStylesIntoShadowDOM(editorElement);
  },

  onUpdate() {
    // Re-inject on updates in case shadow DOM was recreated
    const editorElement = this.editor.view.dom;
    injectStylesIntoShadowDOM(editorElement);
  },
});

export default class UmbTiptapDetailsExtensionApi extends UmbTiptapExtensionApiBase {
  getTiptapExtensions = () => [
    StyleInjector,
    Details, 
    DetailsContent, 
    DetailsSummary
  ];
}