diff --git a/examples/03-ui-components/14-experimental-mobile-formatting-toolbar/.bnexample.json b/examples/03-ui-components/14-experimental-mobile-formatting-toolbar/.bnexample.json
new file mode 100644
index 0000000000..16f9aea065
--- /dev/null
+++ b/examples/03-ui-components/14-experimental-mobile-formatting-toolbar/.bnexample.json
@@ -0,0 +1,11 @@
+{
+ "playground": true,
+ "docs": true,
+ "author": "areknawo",
+ "tags": [
+ "Intermediate",
+ "UI Components",
+ "Formatting Toolbar",
+ "Appearance & Styling"
+ ]
+}
diff --git a/examples/03-ui-components/14-experimental-mobile-formatting-toolbar/App.tsx b/examples/03-ui-components/14-experimental-mobile-formatting-toolbar/App.tsx
new file mode 100644
index 0000000000..62c82ddfe8
--- /dev/null
+++ b/examples/03-ui-components/14-experimental-mobile-formatting-toolbar/App.tsx
@@ -0,0 +1,42 @@
+import "@blocknote/core/fonts/inter.css";
+import {
+ ExperimentalMobileFormattingToolbarController,
+ useCreateBlockNote,
+} from "@blocknote/react";
+import { BlockNoteView } from "@blocknote/mantine";
+import "@blocknote/mantine/style.css";
+
+import "./style.css";
+
+export default function App() {
+ // Creates a new editor instance.
+ const editor = useCreateBlockNote({
+ initialContent: [
+ {
+ type: "paragraph",
+ content: "Welcome to this demo!",
+ },
+ {
+ type: "paragraph",
+ content:
+ "Check out the experimental mobile formatting toolbar by selecting some text (best experienced on a mobile device).",
+ },
+ {
+ type: "paragraph",
+ },
+ ],
+ });
+
+ // Renders the editor instance using a React component.
+ return (
+ // Disables the default formatting toolbar and re-adds it without the
+ // `FormattingToolbarController` component. You may have seen
+ // `FormattingToolbarController` used in other examples, but we omit it here
+ // as we want to control the position and visibility ourselves. BlockNote
+ // also uses the `FormattingToolbarController` when displaying the
+ // Formatting Toolbar by default.
+
+
+
+ );
+}
diff --git a/examples/03-ui-components/14-experimental-mobile-formatting-toolbar/README.md b/examples/03-ui-components/14-experimental-mobile-formatting-toolbar/README.md
new file mode 100644
index 0000000000..7998433781
--- /dev/null
+++ b/examples/03-ui-components/14-experimental-mobile-formatting-toolbar/README.md
@@ -0,0 +1,10 @@
+# Experimental Mobile Formatting Toolbar
+
+This example shows how to use the experimental mobile formatting toolbar, which uses [Visual Viewport API](https://developer.mozilla.org/en-US/docs/Web/API/Visual_Viewport_API) to position the toolbar right above the virtual keyboard on mobile devices.
+
+Controller is currently marked **experimental** due to the flickering issue with positioning (caused by delays of the Visual Viewport API)
+
+**Relevant Docs:**
+
+- [Changing the Formatting Toolbar](/docs/ui-components/formatting-toolbar#changing-the-formatting-toolbar)
+- [Editor Setup](/docs/editor-basics/setup)
diff --git a/examples/03-ui-components/14-experimental-mobile-formatting-toolbar/index.html b/examples/03-ui-components/14-experimental-mobile-formatting-toolbar/index.html
new file mode 100644
index 0000000000..6914836688
--- /dev/null
+++ b/examples/03-ui-components/14-experimental-mobile-formatting-toolbar/index.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+ Experimental Mobile Formatting Toolbar
+
+
+
+
+
+
diff --git a/examples/03-ui-components/14-experimental-mobile-formatting-toolbar/main.tsx b/examples/03-ui-components/14-experimental-mobile-formatting-toolbar/main.tsx
new file mode 100644
index 0000000000..f88b490fbd
--- /dev/null
+++ b/examples/03-ui-components/14-experimental-mobile-formatting-toolbar/main.tsx
@@ -0,0 +1,11 @@
+// AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY
+import React from "react";
+import { createRoot } from "react-dom/client";
+import App from "./App";
+
+const root = createRoot(document.getElementById("root")!);
+root.render(
+
+
+
+);
diff --git a/examples/03-ui-components/14-experimental-mobile-formatting-toolbar/package.json b/examples/03-ui-components/14-experimental-mobile-formatting-toolbar/package.json
new file mode 100644
index 0000000000..e6377e75b1
--- /dev/null
+++ b/examples/03-ui-components/14-experimental-mobile-formatting-toolbar/package.json
@@ -0,0 +1,37 @@
+{
+ "name": "@blocknote/example-experimental-mobile-formatting-toolbar",
+ "description": "AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY",
+ "private": true,
+ "version": "0.12.4",
+ "scripts": {
+ "start": "vite",
+ "dev": "vite",
+ "build": "tsc && vite build",
+ "preview": "vite preview",
+ "lint": "eslint . --max-warnings 0"
+ },
+ "dependencies": {
+ "@blocknote/core": "latest",
+ "@blocknote/react": "latest",
+ "@blocknote/ariakit": "latest",
+ "@blocknote/mantine": "latest",
+ "@blocknote/shadcn": "latest",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1"
+ },
+ "devDependencies": {
+ "@types/react": "^18.0.25",
+ "@types/react-dom": "^18.0.9",
+ "@vitejs/plugin-react": "^4.3.1",
+ "eslint": "^8.10.0",
+ "vite": "^5.3.4"
+ },
+ "eslintConfig": {
+ "extends": [
+ "../../../.eslintrc.js"
+ ]
+ },
+ "eslintIgnore": [
+ "dist"
+ ]
+}
\ No newline at end of file
diff --git a/examples/03-ui-components/14-experimental-mobile-formatting-toolbar/style.css b/examples/03-ui-components/14-experimental-mobile-formatting-toolbar/style.css
new file mode 100644
index 0000000000..839dd7baa0
--- /dev/null
+++ b/examples/03-ui-components/14-experimental-mobile-formatting-toolbar/style.css
@@ -0,0 +1,9 @@
+.bn-container {
+ display: flex;
+ flex-direction: column-reverse;
+ gap: 8px;
+}
+
+.bn-formatting-toolbar {
+ margin-inline: auto;
+}
\ No newline at end of file
diff --git a/examples/03-ui-components/14-experimental-mobile-formatting-toolbar/tsconfig.json b/examples/03-ui-components/14-experimental-mobile-formatting-toolbar/tsconfig.json
new file mode 100644
index 0000000000..1bd8ab3c57
--- /dev/null
+++ b/examples/03-ui-components/14-experimental-mobile-formatting-toolbar/tsconfig.json
@@ -0,0 +1,36 @@
+{
+ "__comment": "AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY",
+ "compilerOptions": {
+ "target": "ESNext",
+ "useDefineForClassFields": true,
+ "lib": [
+ "DOM",
+ "DOM.Iterable",
+ "ESNext"
+ ],
+ "allowJs": false,
+ "skipLibCheck": true,
+ "esModuleInterop": false,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "module": "ESNext",
+ "moduleResolution": "Node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx",
+ "composite": true
+ },
+ "include": [
+ "."
+ ],
+ "__ADD_FOR_LOCAL_DEV_references": [
+ {
+ "path": "../../../packages/core/"
+ },
+ {
+ "path": "../../../packages/react/"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/examples/03-ui-components/14-experimental-mobile-formatting-toolbar/vite.config.ts b/examples/03-ui-components/14-experimental-mobile-formatting-toolbar/vite.config.ts
new file mode 100644
index 0000000000..f62ab20bc2
--- /dev/null
+++ b/examples/03-ui-components/14-experimental-mobile-formatting-toolbar/vite.config.ts
@@ -0,0 +1,32 @@
+// AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY
+import react from "@vitejs/plugin-react";
+import * as fs from "fs";
+import * as path from "path";
+import { defineConfig } from "vite";
+// import eslintPlugin from "vite-plugin-eslint";
+// https://vitejs.dev/config/
+export default defineConfig((conf) => ({
+ plugins: [react()],
+ optimizeDeps: {},
+ build: {
+ sourcemap: true,
+ },
+ resolve: {
+ alias:
+ conf.command === "build" ||
+ !fs.existsSync(path.resolve(__dirname, "../../packages/core/src"))
+ ? {}
+ : ({
+ // Comment out the lines below to load a built version of blocknote
+ // or, keep as is to load live from sources with live reload working
+ "@blocknote/core": path.resolve(
+ __dirname,
+ "../../packages/core/src/"
+ ),
+ "@blocknote/react": path.resolve(
+ __dirname,
+ "../../packages/react/src/"
+ ),
+ } as any),
+ },
+}));
diff --git a/package-lock.json b/package-lock.json
index 2addfdcf33..fc84d7aa47 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -24769,9 +24769,9 @@
}
},
"node_modules/prosemirror-view": {
- "version": "1.34.2",
- "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.34.2.tgz",
- "integrity": "sha512-tPX/V2Xd70vrAGQ/V9CppJtPKnQyQMypJGlLylvdI94k6JaG+4P6fVmXPR1zc1eVTW0gq3c6zsfqwJKCRLaG9Q==",
+ "version": "1.38.0",
+ "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.38.0.tgz",
+ "integrity": "sha512-O45kxXQTaP9wPdXhp8TKqCR+/unS/gnfg9Q93svQcB3j0mlp2XSPAmsPefxHADwzC+fbNS404jqRxm3UQaGvgw==",
"dependencies": {
"prosemirror-model": "^1.20.0",
"prosemirror-state": "^1.0.0",
@@ -30151,7 +30151,7 @@
"prosemirror-state": "^1.4.3",
"prosemirror-tables": "^1.6.1",
"prosemirror-transform": "^1.9.0",
- "prosemirror-view": "^1.33.7",
+ "prosemirror-view": "^1.38.0",
"rehype-format": "^5.0.0",
"rehype-parse": "^8.0.4",
"rehype-remark": "^9.1.2",
@@ -30544,7 +30544,7 @@
"prosemirror-state": "^1.4.3",
"prosemirror-tables": "^1.3.7",
"prosemirror-transform": "^1.9.0",
- "prosemirror-view": "^1.33.7",
+ "prosemirror-view": "^1.38.0",
"react-icons": "^5.2.1"
},
"devDependencies": {
diff --git a/packages/ariakit/src/style.css b/packages/ariakit/src/style.css
index bf1dcd4ced..f966bee877 100644
--- a/packages/ariakit/src/style.css
+++ b/packages/ariakit/src/style.css
@@ -11,6 +11,10 @@
gap: 0.5rem;
}
+.bn-toolbar.bn-ak-toolbar {
+ overflow-x: auto;
+ max-width: 100vw;
+}
.bn-toolbar .bn-ak-button {
width: unset;
}
diff --git a/packages/core/package.json b/packages/core/package.json
index f7ea47f06a..cda0a94fb3 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -85,7 +85,7 @@
"prosemirror-state": "^1.4.3",
"prosemirror-tables": "^1.6.1",
"prosemirror-transform": "^1.9.0",
- "prosemirror-view": "^1.33.7",
+ "prosemirror-view": "^1.38.0",
"rehype-format": "^5.0.0",
"rehype-parse": "^8.0.4",
"rehype-remark": "^9.1.2",
diff --git a/packages/core/src/api/clipboard/toClipboard/copyExtension.ts b/packages/core/src/api/clipboard/toClipboard/copyExtension.ts
index 87e2c7c81e..ed26ccb8c6 100644
--- a/packages/core/src/api/clipboard/toClipboard/copyExtension.ts
+++ b/packages/core/src/api/clipboard/toClipboard/copyExtension.ts
@@ -125,8 +125,7 @@ export function selectedFragmentToHTML<
}
// Uses default ProseMirror clipboard serialization.
- const clipboardHTML: string = (pmView as any).__serializeForClipboard(
- view,
+ const clipboardHTML: string = view.serializeForClipboard(
view.state.selection.content()
).dom.innerHTML;
diff --git a/packages/core/src/blocks/TableBlockContent/TableBlockContent.ts b/packages/core/src/blocks/TableBlockContent/TableBlockContent.ts
index 174cf0654a..48c7c41b24 100644
--- a/packages/core/src/blocks/TableBlockContent/TableBlockContent.ts
+++ b/packages/core/src/blocks/TableBlockContent/TableBlockContent.ts
@@ -5,6 +5,7 @@ import { TableRow } from "@tiptap/extension-table-row";
import { Node as PMNode } from "prosemirror-model";
import { TableView } from "prosemirror-tables";
+import { NodeView } from "prosemirror-view";
import {
createBlockSpecFromStronglyTypedTiptapNode,
createStronglyTypedTiptapNode,
@@ -101,7 +102,7 @@ export const TableBlockContent = createStronglyTypedTiptapNode({
return new BlockNoteTableView(node, EMPTY_CELL_WIDTH, {
...(this.options.domAttributes?.blockContent || {}),
...HTMLAttributes,
- });
+ }) as NodeView;
};
},
});
diff --git a/packages/core/src/extensions/SideMenu/dragging.ts b/packages/core/src/extensions/SideMenu/dragging.ts
index 285e5fba68..581ed81bd3 100644
--- a/packages/core/src/extensions/SideMenu/dragging.ts
+++ b/packages/core/src/extensions/SideMenu/dragging.ts
@@ -1,6 +1,5 @@
import { Node } from "prosemirror-model";
import { NodeSelection, Selection } from "prosemirror-state";
-import * as pmView from "prosemirror-view";
import { EditorView } from "prosemirror-view";
import { createExternalHTMLExporter } from "../../api/exporters/html/externalHTMLExporter.js";
@@ -177,10 +176,8 @@ export function dragStart<
const selectedSlice = view.state.selection.content();
const schema = editor.pmSchema;
- const clipboardHTML = (pmView as any).__serializeForClipboard(
- view,
- selectedSlice
- ).dom.innerHTML;
+ const clipboardHTML =
+ view.serializeForClipboard(selectedSlice).dom.innerHTML;
const externalHTMLExporter = createExternalHTMLExporter(schema, editor);
diff --git a/packages/mantine/src/style.css b/packages/mantine/src/style.css
index 55b5301fb0..9fe02136fa 100644
--- a/packages/mantine/src/style.css
+++ b/packages/mantine/src/style.css
@@ -134,6 +134,10 @@
overflow: auto;
}
+.bn-mantine .mantine-Button-root[aria-controls*="dropdown"] {
+ min-width: fit-content;
+}
+
/* Toolbar styling */
.bn-mantine .bn-toolbar {
background-color: var(--bn-colors-menu-background);
@@ -144,6 +148,8 @@
gap: 2px;
padding: 2px;
width: fit-content;
+ overflow-x: auto;
+ max-width: 100vw;
}
.bn-mantine .bn-toolbar:empty {
diff --git a/packages/react/src/components/FormattingToolbar/ExperimentalMobileFormattingToolbarController.tsx b/packages/react/src/components/FormattingToolbar/ExperimentalMobileFormattingToolbarController.tsx
new file mode 100644
index 0000000000..7110ecdf88
--- /dev/null
+++ b/packages/react/src/components/FormattingToolbar/ExperimentalMobileFormattingToolbarController.tsx
@@ -0,0 +1,89 @@
+import { BlockSchema, InlineContentSchema, StyleSchema } from "@blocknote/core";
+import { UseFloatingOptions } from "@floating-ui/react";
+import { FC, CSSProperties, useMemo, useRef, useState, useEffect } from "react";
+import { useBlockNoteEditor } from "../../hooks/useBlockNoteEditor.js";
+import { useUIPluginState } from "../../hooks/useUIPluginState.js";
+import { FormattingToolbar } from "./FormattingToolbar.js";
+import { FormattingToolbarProps } from "./FormattingToolbarProps.js";
+
+/**
+ * Experimental formatting toolbar controller for mobile devices.
+ * Uses Visual Viewport API to position the toolbar above the virtual keyboard.
+ *
+ * Currently marked experimental due to the flickering issue with positioning cause by the use of the API (and likely a delay in its updates).
+ */
+export const ExperimentalMobileFormattingToolbarController = (props: {
+ formattingToolbar?: FC;
+ floatingOptions?: Partial;
+}) => {
+ const [transform, setTransform] = useState("none");
+ const divRef = useRef(null);
+ const editor = useBlockNoteEditor<
+ BlockSchema,
+ InlineContentSchema,
+ StyleSchema
+ >();
+ const state = useUIPluginState(
+ editor.formattingToolbar.onUpdate.bind(editor.formattingToolbar)
+ );
+ const style = useMemo(() => {
+ return {
+ display: "flex",
+ position: "fixed",
+ bottom: 0,
+ zIndex: 3000,
+ transform,
+ };
+ }, [transform]);
+
+ useEffect(() => {
+ const viewport = window.visualViewport!;
+ function viewportHandler() {
+ // Calculate the offset necessary to set the toolbar above the virtual keyboard (using the offset info from the visualViewport)
+ const layoutViewport = document.body;
+ const offsetLeft = viewport.offsetLeft;
+ const offsetTop =
+ viewport.height -
+ layoutViewport.getBoundingClientRect().height +
+ viewport.offsetTop;
+
+ setTransform(
+ `translate(${offsetLeft}px, ${offsetTop}px) scale(${
+ 1 / viewport.scale
+ })`
+ );
+ }
+ window.visualViewport!.addEventListener("scroll", viewportHandler);
+ window.visualViewport!.addEventListener("resize", viewportHandler);
+ viewportHandler();
+
+ return () => {
+ window.visualViewport!.removeEventListener("scroll", viewportHandler);
+ window.visualViewport!.removeEventListener("resize", viewportHandler);
+ };
+ }, []);
+
+ if (!state) {
+ return null;
+ }
+
+ if (!state.show && divRef.current) {
+ // The component is fading out. Use the previous state to render the toolbar with innerHTML,
+ // because otherwise the toolbar will quickly flickr (i.e.: show a different state) while fading out,
+ // which looks weird
+ return (
+
+ );
+ }
+
+ const Component = props.formattingToolbar || FormattingToolbar;
+
+ return (
+
+
+
+ );
+};
diff --git a/packages/react/src/components/FormattingToolbar/FormattingToolbarController.tsx b/packages/react/src/components/FormattingToolbar/FormattingToolbarController.tsx
index f4bab49b83..43937bc3f7 100644
--- a/packages/react/src/components/FormattingToolbar/FormattingToolbarController.tsx
+++ b/packages/react/src/components/FormattingToolbar/FormattingToolbarController.tsx
@@ -4,7 +4,7 @@ import {
InlineContentSchema,
StyleSchema,
} from "@blocknote/core";
-import { UseFloatingOptions, flip, offset } from "@floating-ui/react";
+import { UseFloatingOptions, flip, offset, shift } from "@floating-ui/react";
import { FC, useMemo, useRef, useState } from "react";
import { useBlockNoteEditor } from "../../hooks/useBlockNoteEditor.js";
@@ -80,7 +80,7 @@ export const FormattingToolbarController = (props: {
3000,
{
placement,
- middleware: [offset(10), flip()],
+ middleware: [offset(10), shift(), flip()],
onOpenChange: (open, _event) => {
// console.log("change", event);
if (!open) {
diff --git a/packages/react/src/hooks/useUIElementPositioning.ts b/packages/react/src/hooks/useUIElementPositioning.ts
index 7c2f47d56e..328e481eb9 100644
--- a/packages/react/src/hooks/useUIElementPositioning.ts
+++ b/packages/react/src/hooks/useUIElementPositioning.ts
@@ -17,7 +17,6 @@ export function useUIElementPositioning(
open: show,
...options,
});
-
const { isMounted, styles } = useTransitionStyles(context);
// handle "escape" and other dismiss events, these will add some listeners to
@@ -35,14 +34,13 @@ export function useUIElementPositioning(
if (referencePos === null) {
return;
}
-
refs.setReference({
getBoundingClientRect: () => referencePos,
});
}, [referencePos, refs]);
- return useMemo(
- () => ({
+ return useMemo(() => {
+ return {
isMounted,
ref: refs.setFloating,
style: {
@@ -53,15 +51,14 @@ export function useUIElementPositioning(
},
getFloatingProps,
getReferenceProps,
- }),
- [
- floatingStyles,
- isMounted,
- refs.setFloating,
- styles,
- zIndex,
- getFloatingProps,
- getReferenceProps,
- ]
- );
+ };
+ }, [
+ floatingStyles,
+ isMounted,
+ refs.setFloating,
+ styles,
+ zIndex,
+ getFloatingProps,
+ getReferenceProps,
+ ]);
}
diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts
index 713325abfa..1ae092b267 100644
--- a/packages/react/src/index.ts
+++ b/packages/react/src/index.ts
@@ -26,6 +26,7 @@ export * from "./components/FormattingToolbar/DefaultButtons/TextAlignButton.js"
export * from "./components/FormattingToolbar/DefaultSelects/BlockTypeSelect.js";
export * from "./components/FormattingToolbar/FormattingToolbar.js";
export * from "./components/FormattingToolbar/FormattingToolbarController.js";
+export * from "./components/FormattingToolbar/ExperimentalMobileFormattingToolbarController.js";
export * from "./components/FormattingToolbar/FormattingToolbarProps.js";
export * from "./components/LinkToolbar/DefaultButtons/DeleteLinkButton.js";
diff --git a/packages/react/src/schema/ReactBlockSpec.tsx b/packages/react/src/schema/ReactBlockSpec.tsx
index 03c75c7039..91bac94d0e 100644
--- a/packages/react/src/schema/ReactBlockSpec.tsx
+++ b/packages/react/src/schema/ReactBlockSpec.tsx
@@ -20,7 +20,6 @@ import {
wrapInBlockStructure,
} from "@blocknote/core";
import {
- NodeView,
NodeViewContent,
NodeViewProps,
NodeViewWrapper,
@@ -190,7 +189,7 @@ export function createReactBlockSpec<
{
className: "bn-react-node-view-renderer",
}
- )(props) as NodeView;
+ )(props);
if (blockConfig.isSelectable === false) {
applyNonSelectableBlockFix(nodeView, this.editor);
diff --git a/packages/shadcn/src/style.css b/packages/shadcn/src/style.css
index 102e80d8b9..f353cccd33 100644
--- a/packages/shadcn/src/style.css
+++ b/packages/shadcn/src/style.css
@@ -169,3 +169,7 @@
.bn-shadcn .bn-extend-button-add-remove-rows {
cursor: row-resize;
}
+.bn-shadcn .bn-toolbar {
+ overflow-x: auto;
+ max-width: 100vw;
+}
diff --git a/packages/xl-multi-column/package.json b/packages/xl-multi-column/package.json
index a702384c07..9d100fcf36 100644
--- a/packages/xl-multi-column/package.json
+++ b/packages/xl-multi-column/package.json
@@ -52,7 +52,7 @@
"prosemirror-state": "^1.4.3",
"prosemirror-tables": "^1.3.7",
"prosemirror-transform": "^1.9.0",
- "prosemirror-view": "^1.33.7",
+ "prosemirror-view": "^1.38.0",
"react-icons": "^5.2.1"
},
"devDependencies": {
diff --git a/playground/src/examples.gen.tsx b/playground/src/examples.gen.tsx
index 67e6090d2e..c95bd0a46b 100644
--- a/playground/src/examples.gen.tsx
+++ b/playground/src/examples.gen.tsx
@@ -621,6 +621,27 @@
"slug": "ui-components"
}
},
+ {
+ "projectSlug": "experimental-mobile-formatting-toolbar",
+ "fullSlug": "ui-components/experimental-mobile-formatting-toolbar",
+ "pathFromRoot": "examples/03-ui-components/14-experimental-mobile-formatting-toolbar",
+ "config": {
+ "playground": true,
+ "docs": true,
+ "author": "areknawo",
+ "tags": [
+ "Intermediate",
+ "UI Components",
+ "Formatting Toolbar",
+ "Appearance & Styling"
+ ]
+ },
+ "title": "Experimental Mobile Formatting Toolbar",
+ "group": {
+ "pathFromRoot": "examples/03-ui-components",
+ "slug": "ui-components"
+ }
+ },
{
"projectSlug": "link-toolbar-buttons",
"fullSlug": "ui-components/link-toolbar-buttons",
diff --git a/playground/src/style.css b/playground/src/style.css
index de46ca3abe..7e716f2b47 100644
--- a/playground/src/style.css
+++ b/playground/src/style.css
@@ -13,3 +13,13 @@ body {
.mantine-AppShell-navbar {
background-color: #f7f7f5;
}
+
+@media (max-width: 767px) {
+ .mantine-AppShell-navbar {
+ display: none !important;
+ }
+
+ .mantine-AppShell-main {
+ padding: 0 !important;
+ }
+}