Repository navigation
feat(core): built-in table drag visualization #2963
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| { | ||
| "playground": true, | ||
| "docs": false, | ||
| "author": "must", | ||
| "tags": [ | ||
| "Intermediate", | ||
| "UI Components", | ||
| "Tables", | ||
| "Drag & Drop", | ||
| "Appearance & Styling" | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| # Table Reordering Visualization | ||
|
|
||
| BlockNote gives table row/column dragging visual feedback out of the box: a | ||
| snapshot of the row/column follows the cursor, the row/column being dragged is | ||
| tinted and outlined, and a drop indicator marks where it would land. | ||
|
|
||
| This example shows how to restyle a table - and those built-in drag | ||
| affordances - to match your own product, using a Microsoft Loop-inspired look: | ||
|
|
||
| - **Restyled tables**: rounded card look, muted header row, hairline | ||
| borders, and a row-hover highlight instead of a harsh black grid. | ||
| - **Retuned drag affordances**: the built-in drag source highlight, drop | ||
| indicator and drag snapshot recolored to the same palette. | ||
|
|
||
| ## How It Works | ||
|
|
||
| Everything here is CSS - no additional extensions or event handling. | ||
| BlockNote's `TableHandlesExtension` owns the whole drag lifecycle and | ||
| exposes it through classes you can target: | ||
|
|
||
| | Class | What it's on | | ||
| | -------------------------- | ---------------------------------------------- | | ||
| | `bn-table-drag-source-row` | every cell of the row being dragged | | ||
| | `bn-table-drag-source-col` | every cell of the column being dragged | | ||
| | `bn-table-drop-cursor` | a bar on the edge the row/column would drop at | | ||
| | `bn-table-drag-preview` | the snapshot shown next to the cursor | | ||
|
|
||
| The first three are ProseMirror decorations inside the editor, so they're | ||
| scoped under `.bn-editor [data-content-type="table"]` like any other table | ||
| style. The snapshot lives outside the live editor, but copies its DOM styling | ||
| context, so those same table styles apply to it. Its outer wrapper also carries | ||
| `bn-table-drag-preview` for preview-specific overrides. The source tint uses | ||
| `--bn-table-drag-source-color` so it layers over existing cell backgrounds. | ||
|
|
||
| `tableStyles.css` does the restyling; `App.tsx` provides an initial table with | ||
| `headerRows: 1` so the header styling is visible right away. | ||
|
|
||
| ## Known Limitations | ||
|
|
||
| - **Keyboard and touch**: BlockNote's table drag handles are `draggable` + | ||
| `onDragStart` only today (see `TableHandle.tsx`) - there's no | ||
| keyboard-operable reorder path, and native HTML5 drag-and-drop isn't | ||
| supported on touch browsers at all. Both are gaps in BlockNote's table-drag | ||
| feature as a whole, not something this example introduces or fixes. | ||
| - **Accessibility**: for the same reason, there's no keyboard focus | ||
| restoration to verify after a reorder - the interaction can't be reached by | ||
| keyboard in the first place yet. | ||
|
|
||
| **Relevant Docs:** | ||
|
|
||
| - [Tables](/docs/features/blocks/tables) | ||
| - [Overriding CSS](/docs/react/styling-theming/overriding-css) | ||
| - [Editor Setup](/docs/getting-started/editor-setup) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| <!doctype html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8" /> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
| <title>Table Reordering Visualization</title> | ||
| <script> | ||
| <!-- AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY --> | ||
| </script> | ||
| </head> | ||
| <body> | ||
| <div id="root"></div> | ||
| <script type="module" src="./main.tsx"></script> | ||
| </body> | ||
| </html> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| // AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY | ||
| import React from "react"; | ||
| import { createRoot } from "react-dom/client"; | ||
| import App from "./src/App.jsx"; | ||
|
|
||
| const root = createRoot(document.getElementById("root")!); | ||
| root.render( | ||
| <React.StrictMode> | ||
| <App /> | ||
| </React.StrictMode>, | ||
| ); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| { | ||
| "name": "@blocknote/example-ui-components-table-reordering-visualization", | ||
| "description": "AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY", | ||
| "type": "module", | ||
| "private": true, | ||
| "version": "0.12.4", | ||
| "scripts": { | ||
| "start": "vp dev", | ||
| "dev": "vp dev", | ||
| "lint": "vp lint --type-aware src main.tsx vite.config.ts", | ||
| "build:prod": "vp run lint && vp build", | ||
| "preview": "vp preview" | ||
| }, | ||
| "dependencies": { | ||
| "@blocknote/ariakit": "latest", | ||
| "@blocknote/core": "latest", | ||
| "@blocknote/mantine": "latest", | ||
| "@blocknote/react": "latest", | ||
| "@blocknote/shadcn": "latest", | ||
| "@mantine/core": "^9.0.2", | ||
| "@mantine/hooks": "^9.0.2", | ||
| "react": "^19.2.3", | ||
| "react-dom": "^19.2.3" | ||
| }, | ||
| "devDependencies": { | ||
| "@types/react": "^19.2.3", | ||
| "@types/react-dom": "^19.2.3", | ||
| "@vitejs/plugin-react": "^6.0.1", | ||
| "vite-plus": "catalog:" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| import "@blocknote/core/fonts/inter.css"; | ||
| import { BlockNoteView } from "@blocknote/mantine"; | ||
| import "@blocknote/mantine/style.css"; | ||
| import { useCreateBlockNote } from "@blocknote/react"; | ||
|
|
||
| import "./tableStyles.css"; | ||
|
|
||
| export default function App() { | ||
| const editor = useCreateBlockNote({ | ||
| tables: { | ||
| splitCells: true, | ||
| cellBackgroundColor: true, | ||
| cellTextColor: true, | ||
| headers: true, | ||
| }, | ||
| initialContent: [ | ||
| { | ||
| type: "heading", | ||
| props: { level: 2 }, | ||
| content: "Restyling BlockNote.js Table Reordering", | ||
| }, | ||
| { | ||
| type: "table", | ||
| content: { | ||
| type: "tableContent", | ||
| columnWidths: [180, 140, 140, 220], | ||
| headerRows: 1, | ||
| rows: [ | ||
| { cells: ["Column A", "Column B", "Column C", "Column D"] }, | ||
| { cells: ["1a", "1b", "1c", "1d"] }, | ||
| { cells: ["2a", "2b", "2c", "2d"] }, | ||
| { cells: ["3a", "3b", "3c", "3d"] }, | ||
| ], | ||
| }, | ||
| }, | ||
| ], | ||
| }); | ||
|
|
||
| return <BlockNoteView editor={editor} />; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| /** | ||
| * Palette | ||
| * | ||
| * Declared on `.bn-root` rather than on the table, for two reasons: the drag | ||
| * handles render in a portal outside the editor, and so does the drag | ||
| * snapshot - BlockNote puts `bn-root` and the active color scheme on both, so | ||
| * anything defined here reaches them. | ||
| */ | ||
| .bn-root { | ||
| --table-border: #e2e2ea; | ||
| --table-shadow: rgb(0 0 0 / 6%); | ||
| --table-header-bg: #f0f0f3; | ||
| --table-header-text: #5d5d70; | ||
| --table-row-hover: #d3d4e0; | ||
| --table-accent: #4a48b8; | ||
| --table-handle-hover-bg: #eef1fa; | ||
| --table-handle-hover-text: #5e5cd0; | ||
| /* Kept translucent so they read as a wash over the cell's own background | ||
| rather than replacing it - a flat fill light enough for this palette would | ||
| blot out the text in dark mode. */ | ||
| --table-drag-tint: rgb(94 92 208 / 14%); | ||
| --table-drag-outline: rgb(94 92 208 / 45%); | ||
| --table-selected: rgb(94 92 208 / 18%); | ||
| } | ||
|
|
||
| .bn-root[data-color-scheme="dark"] { | ||
| --table-border: #3b3b46; | ||
| --table-shadow: rgb(0 0 0 / 40%); | ||
| --table-header-bg: #2c2c35; | ||
| --table-header-text: #a6a6bd; | ||
| --table-row-hover: #35353f; | ||
| /* The light-mode accent is a dark purple, which all but disappears against | ||
| the dark editor background. */ | ||
| --table-accent: #9391ff; | ||
| --table-handle-hover-bg: #35354a; | ||
| --table-handle-hover-text: #b3b1ff; | ||
| --table-drag-tint: rgb(147 145 255 / 22%); | ||
| --table-drag-outline: rgb(147 145 255 / 55%); | ||
| --table-selected: rgb(147 145 255 / 25%); | ||
| } | ||
|
|
||
| /** | ||
| * Tables | ||
| * Loop/Notion-style card look: rounded outer border, muted header row, | ||
| * hairline internal grid and a hover highlight instead of the default | ||
| * harsh black grid lines. | ||
| */ | ||
| .bn-editor [data-content-type="table"] table { | ||
| border-collapse: separate; | ||
| border-spacing: 0; | ||
| border: 1px solid var(--table-border); | ||
| border-radius: 8px; | ||
| box-shadow: 0 1px 3px var(--table-shadow); | ||
| overflow: hidden; | ||
| } | ||
| .bn-editor [data-content-type="table"] th, | ||
| .bn-editor [data-content-type="table"] td { | ||
| border: none; | ||
| border-right: 1px solid var(--table-border); | ||
| border-bottom: 1px solid var(--table-border); | ||
| padding: 10px 16px; | ||
| transition: background-color 0.15s ease; | ||
| } | ||
| .bn-editor [data-content-type="table"] th:last-child, | ||
| .bn-editor [data-content-type="table"] td:last-child { | ||
| border-right: none; | ||
| } | ||
| .bn-editor [data-content-type="table"] tr:last-child th, | ||
| .bn-editor [data-content-type="table"] tr:last-child td { | ||
| border-bottom: none; | ||
| } | ||
| .bn-editor [data-content-type="table"] th { | ||
| background-color: var(--table-header-bg); | ||
| color: var(--table-header-text); | ||
|
Comment on lines
+73
to
+74
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Preserve user-selected cell colors. These selectors override BlockNote’s color attributes. Header cells ignore selected text and background colors, and body cells lose their background color on hover. The example enables both color controls in Apply header defaults only when the corresponding color attribute is absent. Preserve colored backgrounds during hover. Also applies to: 79-80
|
||
| font-weight: 600; | ||
| font-size: 0.8125em; | ||
| letter-spacing: 0.01em; | ||
| } | ||
| .bn-editor [data-content-type="table"] tr:hover > td { | ||
| background-color: var(--table-row-hover); | ||
| } | ||
| .bn-editor [data-content-type="table"] .selectedCell:after { | ||
| background: var(--table-selected); | ||
| opacity: 1; | ||
| } | ||
|
|
||
| /** | ||
| * Row/column reordering: BlockNote already highlights the row/column being | ||
| * dragged and marks the drop position; these just retune the built-in | ||
| * affordances to the palette above, so the drag state stays distinguishable | ||
| * from this table's own hover and selection colors. | ||
| */ | ||
| .bn-editor [data-content-type="table"] td.bn-table-drag-source-row, | ||
| .bn-editor [data-content-type="table"] th.bn-table-drag-source-row, | ||
| .bn-editor [data-content-type="table"] td.bn-table-drag-source-col, | ||
| .bn-editor [data-content-type="table"] th.bn-table-drag-source-col { | ||
| --bn-table-drag-source-color: var(--table-drag-tint); | ||
| outline-color: var(--table-drag-outline); | ||
| } | ||
| .bn-editor [data-content-type="table"] .bn-table-drop-cursor { | ||
| background-color: var(--table-accent); | ||
| } | ||
|
|
||
| /* Row/column drag handles and add-row/add-column buttons. */ | ||
| .bn-mantine .bn-table-handle, | ||
| .bn-mantine .bn-table-cell-handle { | ||
| border-radius: 2px; | ||
| } | ||
| .bn-mantine .bn-table-handle:hover, | ||
| .bn-mantine .bn-table-handle-dragging, | ||
| .bn-mantine .bn-table-cell-handle:hover, | ||
| .bn-mantine .bn-extend-button:hover, | ||
| .bn-mantine .bn-extend-button-editing { | ||
| background-color: var(--table-handle-hover-bg); | ||
| color: var(--table-handle-hover-text); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| { | ||
| "__comment": "AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY", | ||
| "compilerOptions": { | ||
| "target": "ESNext", | ||
| "useDefineForClassFields": true, | ||
| "lib": ["DOM", "DOM.Iterable", "ESNext"], | ||
| "allowJs": false, | ||
| "skipLibCheck": true, | ||
| "allowSyntheticDefaultImports": true, | ||
| "strict": true, | ||
| "forceConsistentCasingInFileNames": true, | ||
| "module": "ESNext", | ||
| "moduleResolution": "bundler", | ||
| "resolveJsonModule": true, | ||
| "isolatedModules": true, | ||
| "noEmit": true, | ||
| "jsx": "react-jsx", | ||
| "composite": true | ||
| }, | ||
| "include": ["."], | ||
| "__ADD_FOR_LOCAL_DEV_references": [ | ||
| { | ||
| "path": "../../../packages/core/" | ||
| }, | ||
| { | ||
| "path": "../../../packages/react/" | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| /// <reference types="vite-plus/client" /> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| // 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-plus"; | ||
| // https://vitejs.dev/config/ | ||
| export default defineConfig(((conf: { command: string }) => ({ | ||
| // This example's standalone build lints its sources, unlike the workspace | ||
| // lint command which excludes examples. | ||
| lint: { ignorePatterns: [] }, | ||
| 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), | ||
| }, | ||
| })) as Parameters<typeof defineConfig>[0]); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -59,13 +59,29 @@ | |
| -moz-osx-font-smoothing: grayscale; | ||
| } | ||
|
|
||
| /* Marks the edge a dragged table row/column would be dropped at. */ | ||
| .bn-table-drop-cursor { | ||
| position: absolute; | ||
| z-index: 20; | ||
| background-color: #adf; | ||
| pointer-events: none; | ||
| } | ||
|
Comment on lines
+62
to
68
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Description: Look for position rules applied to table cells in the core styles.
rg -n -B 6 'position:\s*relative' packages/core/src/editor/editor.css
rg -n -C 6 'data-content-type="table"\].*\b(td|th)\b' packages/core/src --glob '*.css'Repository: TypeCellOS/BlockNote Length of output: 2371 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- dragDecorations.ts locations ---'
fd -i 'dragDecorations\.ts$' packages
printf '%s\n' '--- table wrapper and drop cursor references ---'
rg -n -C 12 'bn-table-drop-cursor|tableWrapper|drop cursor|dropCursor' packages/core/src --glob '*.ts' --glob '*.tsx' --glob '*.css'
printf '%s\n' '--- relevant editor.css ---'
sed -n '165,225p' packages/core/src/editor/editor.cssRepository: TypeCellOS/BlockNote Length of output: 50376 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- dragDecorations outline ---'
ast-grep outline packages/core/src/extensions/TableHandles/dragDecorations.ts
printf '%s\n' '--- decoration implementation ---'
sed -n '1,260p' packages/core/src/extensions/TableHandles/dragDecorations.ts
printf '%s\n' '--- table wrapper and cell DOM styles ---'
rg -n -C 8 'tableWrapper|table-widgets-container|position:|appendChild|createElement' packages/core/src/extensions/TableHandles packages/core/src/editor/editor.css --glob '*.ts' --glob '*.css'Repository: TypeCellOS/BlockNote Length of output: 28675 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- all repository rules for table cells and wrappers ---'
rg -n -C 5 '(\.tableWrapper|tableWrapper|data-content-type="table").*(td|th)|(td|th).*(position|tableWrapper)|position\s*:\s*(relative|absolute|fixed|sticky)' packages --glob '*.css' --glob '*.scss' --glob '*.less'
printf '%s\n' '--- widget position tests and DOM assumptions ---'
rg -n -C 10 'DROP_CURSOR_CLASS|bn-table-drop-cursor|decorationPos|Decoration\.widget|tableWrapper' packages/core/src/extensions/TableHandles --glob '*.ts' --glob '*.tsx'
printf '%s\n' '--- table node-view DOM construction ---'
rg -n -C 12 'tableWrapper|table-widgets-container|contentDOM|createElement\("table"|createElement\("td"|createElement\("th"' packages/core/src --glob '*.ts' --glob '*.tsx'Repository: TypeCellOS/BlockNote Length of output: 50377 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- table-related source files ---'
fd -i 'table' packages/core/src/blocks packages/core/src/extensions packages/core/src/editor
printf '%s\n' '--- table node-view and wrapper references ---'
rg -n -C 6 'tableWrapper|table-widgets-container|contentDOM|tableHandles' packages/core/src/blocks packages/core/src/extensions/TableHandles --glob '*.ts' --glob '*.tsx'
printf '%s\n' '--- read-only containing-block verifier ---'
python3 - <<'PY'
from pathlib import Path
import re
css = Path("packages/core/src/editor/editor.css").read_text()
drag = Path("packages/core/src/extensions/TableHandles/dragDecorations.ts").read_text()
cell_rule = re.search(
r'\[data-content-type="table"\]\s+(?:th|td)[^{]*,\s*'
r'\[data-content-type="table"\]\s+(?:th|td)\s*\{([^}]*)\}',
css,
re.S,
)
wrapper_rule = re.search(
r'\.tableWrapper\s*\{([^}]*)\}',
css,
re.S,
)
assert cell_rule, "table cell rule not found"
assert not re.search(r'\bposition\s*:', cell_rule.group(1)), (
"table cell rule unexpectedly defines position"
)
assert wrapper_rule and re.search(r'\bposition\s*:\s*relative\s*;', wrapper_rule.group(1)), (
"tableWrapper is not positioned"
)
assert 'Decoration.widget(decorationPos' in drag
assert 'widget.style.left = "0"' in drag
assert 'widget.style.right = "0"' in drag
assert 'widget.style.top = "0"' in drag
assert 'widget.style.bottom = "0"' in drag
print("cell_position_rule=absent")
print("tableWrapper_position=relative")
print("drop_cursor_widget=inserted_at_cell_boundary_with_edge_offsets")
print("nearest_positioned_ancestor=tableWrapper")
PYRepository: TypeCellOS/BlockNote Length of output: 43622 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- table node-view construction ---'
sed -n '190,265p' packages/core/src/blocks/Table/block.ts
printf '%s\n' '--- corrected read-only containing-block verifier ---'
python3 - <<'PY'
from pathlib import Path
import re
css = Path("packages/core/src/editor/editor.css").read_text()
drag = Path("packages/core/src/extensions/TableHandles/dragDecorations.ts").read_text()
table = Path("packages/core/src/blocks/Table/block.ts").read_text()
cell_rules = re.findall(
r'\[data-content-type="table"\]\s+(?:th|td)\s*\{([^}]*)\}',
css,
re.S,
)
assert cell_rules, "table cell rules not found"
assert all(not re.search(r'\bposition\s*:', body) for body in cell_rules), (
"a table cell rule defines position"
)
wrapper_rule = re.search(r'\.tableWrapper\s*\{([^}]*)\}', css, re.S)
assert wrapper_rule and re.search(
r'\bposition\s*:\s*relative\s*;', wrapper_rule.group(1)
), "tableWrapper is not positioned"
assert 'Decoration.widget(decorationPos' in drag
for declaration in (
'widget.style.left = "0"',
'widget.style.right = "0"',
'widget.style.top = "0"',
'widget.style.bottom = "0"',
):
assert declaration in drag, declaration
assert 'tableWrapper.appendChild(tableWrapperInner)' in table
assert 'tableWrapper.appendChild(floatingContainer)' in table
print("cell_position_rule=absent")
print("tableWrapper_position=relative")
print("drop_cursor_widget=inserted_at_cell_boundary_with_edge_offsets")
print("tableWrapper_contains_table_and_floating_container")
print("nearest_positioned_ancestor_for_cell_widget=tableWrapper")
PYRepository: TypeCellOS/BlockNote Length of output: 3250 Set each table cell as the drop cursor’s containing block.
🤖 Prompt for AI Agents |
||
|
|
||
| /* Highlights the source row/column, separately from the destination cursor. */ | ||
| .bn-editor [data-content-type="table"] .bn-table-drag-source-row, | ||
| .bn-editor [data-content-type="table"] .bn-table-drag-source-col { | ||
| /* A separate layer preserves the cell's own background color beneath the | ||
| translucent tint, in both light and dark themes. */ | ||
| background-image: linear-gradient( | ||
| var(--bn-table-drag-source-color, rgb(170 221 255 / 22%)), | ||
| var(--bn-table-drag-source-color, rgb(170 221 255 / 22%)) | ||
| ); | ||
| /* An outline rather than a border, so it doesn't shift the cell's contents, | ||
| and inset so adjacent cells in the dragged row/column don't double it up. */ | ||
| outline: 1.5px dashed #adf; | ||
| outline-offset: -1.5px; | ||
| } | ||
|
|
||
| .bn-editor .bn-collaboration-cursor__base { | ||
| position: relative; | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Add the HTML doctype.
The missing doctype can enable quirks mode. Add
<!doctype html>before the<html>element.Proposed fix
+<!doctype html> <html lang="en">📝 Committable suggestion
🧰 Tools
🪛 HTMLHint (1.9.2)
[error] 1-1: Doctype must be declared before any non-comment content.
(doctype-first)
🤖 Prompt for AI Agents
Source: Linters/SAST tools