Reference

CSS and UI-provider requirements

Design tokens, stylesheet order, Tailwind sources and the UI kit each surface needs.
  • Design tokens: @skaile/forge-editor/styles/tokens.css declares the shared --sk-* variables (--sk-bg, --sk-fg, --sk-muted, --sk-border, --sk-primary, --sk-primary-fg, --sk-success, --sk-warning, --sk-danger, --sk-radius, --sk-font) with light defaults under :where(:root) and dark values under :where(.dark). Every feature stylesheet reads them; a UI kit maps them onto its own variables (Nuxt UI: vue/kit/nuxt-ui.css, React/shadcn: react/styles/ui-vars.css), a host may override them once.
  • Basket colours: @skaile/forge-editor/styles/composites.css declares the colours of the default colour Baskets (compositeFeature({ baskets })) as --sk-basket-red, --sk-basket-orange, --sk-basket-yellow, --sk-basket-green and --sk-basket-blue, light values under :where(:root) and dark values under :where(.dark) like the design tokens, so a host overrides them once. A custom Basket brings its own color. The Typed block's node view sets --sk-basket-color on a Typed block in a Basket; the stripe reads it.
  • Block type tone: a Block type with chrome.tone (BlockType, any CSS colour or var(...)) sets its Typed blocks apart. The Typed block's node view sets data-block-type-tone and --sk-block-type-tone on the wrapper of such a Typed block; composites.css tints its surface with the tone and paints the left border in it (a Basket stripe still wins on the border), and the Document tree rows of the Block type tint the Block type icon. How strong the tint is comes from --sk-block-type-tone-mix (8% under :where(:root), 14% under :where(.dark), like the Basket colours). A Block type without a tone is untouched.
  • Vue UI kit: the Vue binding is built on Nuxt UI. The chrome renders through the kit handed to createEditor({ kit }); kit is optional and defaults to nuxtUiKit() (@skaile/forge-editor/vue/kit/nuxt-ui, needs @nuxt/ui, stylesheet @skaile/forge-editor/vue/kit/nuxt-ui.css). @skaile/forge-editor/vue/kit holds the VueUiKit contract and useUiKit(); pass your own VueUiKit only to render the chrome with another library. Only the nuxt-ui kit entry imports @nuxt/ui. The Nuxt module has no kit option: it always adds vue/kit/nuxt-ui.css, patches Nuxt UI's ui.css so Tailwind scans the package (@nuxt/ui must be registered before the module) and installs the Nuxt UI context plugin.
  • Import the core CSS after Tailwind and Nuxt UI, and let Tailwind see the library's classes. Scan the whole dist of the package (Vue files in dist/vue and dist/features/*/components, React files in dist/react and dist/features/*/react), never a single subdirectory; in this repository the examples scan packages/editor/src:
    @import "tailwindcss";
    @import "@nuxt/ui";
    @import "@skaile/forge-editor/styles/core.css";
    @source "../node_modules/@skaile/forge-editor/dist"; /* relative path from this CSS file to the installed package's dist */
    

    The Nuxt module needs none of this: it adds core.css and every feature sheet to css (themes.css last) and appends the package to Tailwind's @source list. In plain Vue also import the sheet of each feature you use, e.g. @skaile/forge-editor/styles/media.css, after core.css. The AI sidebar (@skaile/forge-editor/ai/vue/chat) renders on the optional peer @skaile/forge-chat, whose resources the host owns, in Nuxt as well: import @skaile/forge-chat/styles/chat.css, add @source for @skaile/forge-chat/dist (e.g. @source "../../../node_modules/@skaile/forge-chat/dist";) and, in Nuxt, '@skaile/forge-chat/nuxt' after '@nuxt/ui' in modules. styles/ai.css no longer imports the chat stylesheet.
  • Plain Vue: Vite plugins vue() and ui({ router: false }) from @nuxt/ui/vite, app.use(ui) with import ui from '@nuxt/ui/vue-plugin', and the editor context app.use(createEditor()) (createEditor from @skaile/forge-editor/vue). Wrap the page in <UApp> (overlays, toasts, tooltips).
  • React: Vite with @vitejs/plugin-react and @tailwindcss/vite. The CSS needs Tailwind, tw-animate-css, @skaile/forge-editor/react/styles/ui-vars.css, core.css (plus the sheet of each feature you use, e.g. mentions.css), the shadcn theme block (:root/.dark tokens and @theme inline; bunx shadcn@latest init generates it) and @source for the library's whole dist (@source "../node_modules/@skaile/forge-editor/dist";): the Features' React components live in dist/features/*/react, not in dist/react. For the advanced editor import @skaile/forge-editor/react/styles/advanced.css instead of those three library sheets: it bundles ui-vars.css, core.css, every feature sheet and themes.css (last). Preload the icons with addCollection(lucide) from @iconify/react, wrap the tree in <EditorContextProvider context={createEditorContext({ notify: reactNotifier })}> and mount <Toaster /> (sonner, from @skaile/forge-editor/react) once so notices show up; see the usage guide, Advanced editor in React, examples/react-simple and examples/react-advanced.
  • Icons: Nuxt UI resolves i-lucide-* names at build time. A workspace library lies outside the Vite root that Nuxt UI scans, so list its icons for the client bundle (see how examples/vue-simple/vite.config.ts collects them) to avoid Iconify API requests at runtime.
  • The editor context is mandatory: Vue EditorSurface, SimpleEditor and EditorToolbar throw a descriptive error without createEditor(), the React components without <EditorContextProvider>. Both wrap the framework-free createEditorContext() from @skaile/forge-editor/core; the host owns the React context and may call context.dispose() itself.
Copyright © 2026