Skip to main content

Overview

Yoopta Editor provides flexible styling options through CSS variables, theme packages, and custom CSS. The editor is built with a headless architecture, allowing you to style it completely to match your application’s design system.

Theme Packages

Yoopta offers pre-built theme packages that provide styled UI components for all plugins:

Available Themes

Shadcn Theme

Production-ready theme using shadcn/ui design tokens
npm install @yoopta/themes-shadcn

Material Theme

Material Design inspired theme (in progress)
npm install @yoopta/themes-material

Applying Themes

Apply to all plugins:
import { createYooptaEditor } from '@yoopta/editor';
import { applyTheme } from '@yoopta/themes-shadcn';
import Paragraph from '@yoopta/paragraph';
import HeadingOne from '@yoopta/headings/HeadingOne';

const PLUGINS = [Paragraph, HeadingOne, /* ... */];

const editor = createYooptaEditor({
  plugins: applyTheme(PLUGINS),
  marks: MARKS,
});
Apply to individual plugins:
import Callout from '@yoopta/callout';
import { CalloutUI } from '@yoopta/themes-shadcn/callout';

const StyledCallout = Callout.extend({
  elements: CalloutUI,
});

CSS Variables

Shadcn Theme Variables

The @yoopta/themes-shadcn package uses standard shadcn CSS variables. If you don’t already have shadcn variables defined, import them:
import '@yoopta/themes-shadcn/variables.css';
Available CSS Variables:
:root {
  /* Colors */
  --background: 0 0% 100%;
  --foreground: 0 0% 3.9%;
  --card: 0 0% 100%;
  --card-foreground: 0 0% 3.9%;
  --popover: 0 0% 100%;
  --popover-foreground: 0 0% 3.9%;
  --primary: 0 0% 9%;
  --primary-foreground: 0 0% 98%;
  --secondary: 0 0% 96.1%;
  --secondary-foreground: 0 0% 9%;
  --muted: 0 0% 96.1%;
  --muted-foreground: 0 0% 45.1%;
  --accent: 0 0% 96.1%;
  --accent-foreground: 0 0% 9%;
  --destructive: 0 84.2% 60.2%;
  --destructive-foreground: 0 0% 98%;
  --border: 0 0% 89.8%;
  --input: 0 0% 89.8%;
  --ring: 0 0% 3.9%;
  
  /* Border radius */
  --radius: 0.5rem;
}
Dark Mode:
.dark,
[data-theme='dark'],
[data-yoopta-theme='dark'] {
  --background: 0 0% 3.9%;
  --foreground: 0 0% 98%;
  --card: 0 0% 3.9%;
  --card-foreground: 0 0% 98%;
  --primary: 0 0% 98%;
  --primary-foreground: 0 0% 9%;
  /* ... other dark mode variables */
}

Core Editor Variables

The editor’s base styles use these variables:
.yoopta-editor {
  color: hsl(var(--foreground));
  background-color: hsl(var(--background));
}

/* Block selection highlight */
.yoopta-block[data-block-selected='true']::before {
  background-color: #2383e224;
}

/* Text selection */
.yoopta-editor::selection {
  background: #c6ddf8;
}

/* Placeholder text */
.yoopta-placeholder::before {
  color: hsl(var(--foreground));
  opacity: 0.4;
}

Custom Styling

Customizing Block Appearance

Target specific block types using data attributes:
/* Style all paragraphs */
[data-yoopta-block-type="Paragraph"] {
  line-height: 1.8;
  font-size: 16px;
}

/* Style headings */
[data-yoopta-block-type="HeadingOne"] {
  font-weight: 700;
  font-size: 2.5rem;
  margin-top: 2rem;
  margin-bottom: 1rem;
}

/* Style selected blocks */
.yoopta-block[data-block-selected='true'] {
  outline: 2px solid var(--primary);
  border-radius: 8px;
}

Customizing Element Appearance

Style specific elements within blocks:
/* Image elements */
[data-element-type="image"] img {
  border-radius: 12px;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}

/* Code blocks */
[data-element-type="code"] {
  border-radius: 8px;
  padding: 1rem;
  background: hsl(var(--muted));
}

/* Callout blocks */
[data-element-type="callout"] {
  border-left: 4px solid hsl(var(--accent));
  padding: 1rem;
  border-radius: 6px;
}

Custom Container Styles

Style the editor container:
<YooptaEditor
  editor={editor}
  className="my-custom-editor"
  style={{
    width: 750,
    maxWidth: '100%',
    margin: '0 auto',
    padding: '2rem',
  }}
>
  {/* ... */}
</YooptaEditor>
.my-custom-editor {
  background: white;
  border-radius: 12px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
  min-height: 500px;
}

.my-custom-editor .yoopta-block {
  margin: 8px 0;
}

Styling UI Components

Customize toolbar and UI components from @yoopta/ui:

Floating Toolbar

/* Customize toolbar appearance */
.yoopta-floating-toolbar {
  background: hsl(var(--popover));
  border: 1px solid hsl(var(--border));
  border-radius: var(--radius);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}

/* Toolbar buttons */
.yoopta-floating-toolbar button {
  color: hsl(var(--foreground));
  transition: background 150ms;
}

.yoopta-floating-toolbar button:hover {
  background: hsl(var(--accent));
}

Action Menu

.yoopta-action-menu-list {
  background: hsl(var(--card));
  border: 1px solid hsl(var(--border));
  border-radius: var(--radius);
  max-height: 400px;
  overflow-y: auto;
}

.yoopta-action-menu-item {
  padding: 8px 12px;
  cursor: pointer;
  border-radius: 4px;
}

.yoopta-action-menu-item:hover {
  background: hsl(var(--accent));
}

Responsive Design

Make your editor responsive:
.yoopta-editor {
  width: 100%;
  max-width: 750px;
  margin: 0 auto;
}

@media (max-width: 768px) {
  .yoopta-editor {
    padding: 1rem;
  }
  
  [data-yoopta-block-type="HeadingOne"] {
    font-size: 2rem;
  }
  
  /* Stack toolbar buttons on mobile */
  .yoopta-floating-toolbar {
    flex-wrap: wrap;
  }
}

@media (max-width: 480px) {
  .yoopta-editor {
    padding: 0.5rem;
  }
  
  [data-yoopta-block-type="HeadingOne"] {
    font-size: 1.75rem;
  }
}
Optimize for printing:
@media print {
  .yoopta-editor {
    width: 100%;
    max-width: none;
    background: white;
    color: black;
  }
  
  /* Hide interactive elements */
  .yoopta-floating-toolbar,
  .yoopta-action-menu-list,
  .yoopta-block-actions {
    display: none !important;
  }
  
  /* Ensure proper page breaks */
  .yoopta-block {
    page-break-inside: avoid;
  }
}

Advanced Customization

Custom Theme Variables

Define your own theme:
:root {
  /* Brand colors */
  --editor-brand: #6366f1;
  --editor-brand-light: #818cf8;
  --editor-brand-dark: #4f46e5;
  
  /* Typography */
  --editor-font-sans: 'Inter', system-ui, sans-serif;
  --editor-font-mono: 'Fira Code', monospace;
  
  /* Spacing */
  --editor-spacing-xs: 0.25rem;
  --editor-spacing-sm: 0.5rem;
  --editor-spacing-md: 1rem;
  --editor-spacing-lg: 1.5rem;
}

.yoopta-editor {
  font-family: var(--editor-font-sans);
}

.yoopta-block[data-block-selected='true']::before {
  background-color: color-mix(in srgb, var(--editor-brand) 15%, transparent);
}

CSS-in-JS

Use styled-components or emotion:
import styled from 'styled-components';

const StyledEditor = styled.div`
  .yoopta-editor {
    background: ${props => props.theme.colors.background};
    border-radius: ${props => props.theme.radii.lg};
  }
  
  .yoopta-block[data-block-selected='true']::before {
    background-color: ${props => props.theme.colors.primary}20;
  }
`;

function Editor() {
  return (
    <StyledEditor>
      <YooptaEditor editor={editor}>
        {/* ... */}
      </YooptaEditor>
    </StyledEditor>
  );
}

Best Practices

Define global CSS variables to maintain consistent spacing, colors, and typography throughout your editor. This makes theme switching and maintenance easier.
Use class prefixes or CSS modules to avoid conflicts with other parts of your application:
.my-app .yoopta-editor { /* styles */ }
Always test your custom styles in both light and dark modes. Use CSS variables that adapt to theme changes automatically.
Ensure sufficient color contrast (WCAG 4.5:1 for normal text, 3:1 for large text). Test keyboard navigation visibility with custom focus styles.
Avoid expensive CSS operations like shadows and transforms on every block. Use will-change sparingly and only when needed.

Examples

Check out these examples for inspiration:

Customization

Learn about customizing plugin behavior

Plugins

Explore available plugins

Build docs developers (and LLMs) love