Lock Callouts edit mode

Hi everyone,

I’m working with a multi-column layout in Obsidian that uses callouts (not the Tasks plugin, since it doesn’t support multi-column setups). In one column, I display tasks, and everything works fine except for one issue with the callouts themselves.

Whenever I accidentally click anywhere inside the callout (especially near a checkbox), it enters edit mode, even though I don’t want to edit it. While this behavior is expected, it gets annoying over time, especially when I just misclick.

I managed to lock all interactions within the callout using CSS (pointer-events: none), which prevents unwanted edits. However, this also blocks specific interactions, such as the calendar date picker icon used in tasks. While other links and checkboxes remain clickable, the calendar icon gets locked as well.
Here is what I’ve tried so far

/* Désactiver les clics partout dans le callout */
.is-live-preview .cm-embed-block.cm-callout {
    pointer-events: none !important; /* Désactive les clics globalement */
}

/* Réactiver les clics sur les liens */
.is-live-preview .cm-embed-block.cm-callout a {
    pointer-events: auto !important;
    cursor: pointer;
}

/* Réactiver les clics sur les checkboxes */
.is-live-preview .cm-embed-block.cm-callout input[type="checkbox"] {
    pointer-events: auto !important;
    cursor: pointer;
}

/* Réactiver les clics sur l'icône de crayon ou boutons spécifiques */
.is-live-preview .cm-embed-block.cm-callout .cg-note-toolbar-callout {
    pointer-events: auto !important;
    cursor: pointer;
}

/* Réactiver les clics sur le calendrier Flatpickr */
.is-live-preview .cm-embed-block.cm-callout .flatpickr-calendar,
.is-live-preview .cm-embed-block.cm-callout .flatpickr-calendar * {
    pointer-events: auto !important; /* Déverrouille le calendrier */
    cursor: pointer;
}

/* Boutons de navigation du calendrier (précédent et suivant) */
.is-live-preview .cm-embed-block.cm-callout .flatpickr-prev-month,
.is-live-preview .cm-embed-block.cm-callout .flatpickr-next-month {
    pointer-events: auto !important;
    cursor: pointer;
}

/* Sélection du mois et de l'année dans le calendrier */
.is-live-preview .cm-embed-block.cm-callout .flatpickr-monthDropdown-months,
.is-live-preview .cm-embed-block.cm-callout .numInput.cur-year {
    pointer-events: auto !important;
    cursor: pointer;
}

/* Jours cliquables dans le calendrier */
.is-live-preview .cm-embed-block.cm-callout .flatpickr-day {
    pointer-events: auto !important;
    cursor: pointer;
}

/* Boutons spécifiques comme "Today" et "Clear" */
.is-live-preview .cm-embed-block.cm-callout .flatpickr-button {
    pointer-events: auto !important;
    cursor: pointer;
}

Does anyone know a way to completely lock a callout to prevent it from entering edit mode (like only editable with edit mode or by clicking on the up right <> icon only) while keeping all interactive elements (e.g., links, checkboxes, calendar icons) functional?

Thanks in advance for your help! :blush:

2 Likes

I wish… same with links and html. They desperately need the same edit button that code blocks have or at least a key combination that prevents them from expanding the hidden markdown. I’m so tired of trying to click an internal link or on something in a call out and instead of following the link or interacting with the thing in the callout it just expands the markdown.

edit callouts even already have the little edit block button so it makes absolutely no sense that just clicking on them also forces them to expand the markdown. That qualifies as a bug to me

2 Likes

having a callouts lock would be great. If I want to edit the callout I could use the edit button, and this would save me a lot of read/edit toggling

1 Like

I found a solution: I used this code and asked claude to help me with it.

here’s the css, it will lock the callout for clicks, but switch to edit mode with the top-right button all while still allowing for clicks on links:

/* === OBSIDIAN CALLOUT CLICK PREVENTION - SURGICAL APPROACH === */
/* Disable clicks everywhere in the callout */
.is-live-preview .cm-embed-block.cm-callout {
    pointer-events: none !important;
}

/* === ESSENTIAL INTERACTIVE ELEMENTS ONLY === */
/* Only enable pointer events on elements that MUST be clickable and have no alternatives */

/* Links - these need to work */
.is-live-preview .cm-embed-block.cm-callout a,
.is-live-preview .cm-embed-block.cm-callout .internal-link,
.is-live-preview .cm-embed-block.cm-callout .external-link {
    pointer-events: auto !important;
    cursor: pointer;
}

/* Form inputs - these need to work */
.is-live-preview .cm-embed-block.cm-callout input[type="checkbox"],
.is-live-preview .cm-embed-block.cm-callout input[type="text"],
.is-live-preview .cm-embed-block.cm-callout input[type="number"],
.is-live-preview .cm-embed-block.cm-callout select,
.is-live-preview .cm-embed-block.cm-callout textarea {
    pointer-events: auto !important;
    cursor: pointer;
}

/* === EDIT BUTTON PRESERVATION === */
.is-live-preview .cm-embed-block.cm-callout .edit-block-button,
.is-live-preview .cm-embed-block.cm-callout .cm-edit-block-button,
.is-live-preview .cm-embed-block.cm-callout .callout-fold,
.is-live-preview .cm-embed-block.cm-callout .callout-title .clickable-icon {
    pointer-events: auto !important;
    cursor: pointer;
    z-index: 10;
}

/* Callout title area - enable for edit button */
.is-live-preview .cm-embed-block.cm-callout .callout-title {
    pointer-events: auto !important;
}

/* But disable the title text itself */
.is-live-preview .cm-embed-block.cm-callout .callout-title .callout-title-inner {
    pointer-events: none !important;
}

/* === DATAVIEW CODE BLOCK CONTAINERS === */
/* Disable all dataview code block containers and backgrounds */
.is-live-preview .cm-embed-block.cm-callout .cm-line:has(.cm-hmd-codeblock),
.is-live-preview .cm-embed-block.cm-callout .cm-hmd-codeblock,
.is-live-preview .cm-embed-block.cm-callout .cm-preview-code-block,
.is-live-preview .cm-embed-block.cm-callout .HyperMD-codeblock,
.is-live-preview .cm-embed-block.cm-callout .HyperMD-codeblock-bg,
.is-live-preview .cm-embed-block.cm-callout .cm-embed-block,
.is-live-preview .cm-embed-block.cm-callout .dataview.inline-query,
.is-live-preview .cm-embed-block.cm-callout .block-language-dataview,
.is-live-preview .cm-embed-block.cm-callout .block-language-dataviewjs {
    pointer-events: none !important;
}

/* Disable dataview table backgrounds completely */
.is-live-preview .cm-embed-block.cm-callout .dataview table,
.is-live-preview .cm-embed-block.cm-callout .dataview tbody,
.is-live-preview .cm-embed-block.cm-callout .dataview tr,
.is-live-preview .cm-embed-block.cm-callout .dataview td,
.is-live-preview .cm-embed-block.cm-callout .dataview th {
    pointer-events: none !important;
}

/* === SELECTIVE DATAVIEW RE-ENABLING === */
/* ONLY re-enable essential clickable elements within dataview */
.is-live-preview .cm-embed-block.cm-callout .dataview a,
.is-live-preview .cm-embed-block.cm-callout .dataview .internal-link,
.is-live-preview .cm-embed-block.cm-callout .dataview .external-link {
    pointer-events: auto !important;
    cursor: pointer;
}

/* Tags can stay disabled - they're usually just visual */
.is-live-preview .cm-embed-block.cm-callout .dataview .tag {
    pointer-events: none !important;
}

/* === CHARTS AND VISUALIZATIONS - SELECTIVE ENABLING === */
/* Enable canvas elements for hover effects but prevent clicks via JavaScript */
.is-live-preview .cm-embed-block.cm-callout canvas {
    pointer-events: auto !important;
    cursor: default;
}

/* Enable SVG elements for hover effects */
.is-live-preview .cm-embed-block.cm-callout svg {
    pointer-events: auto !important;
    cursor: default;
}

/* === MEDICATION HEATMAP - SELECTIVE ENABLING === */
/* Enable the heatmap container for interactions */
.is-live-preview .cm-embed-block.cm-callout [id*="medication-heatmap"] {
    pointer-events: auto !important;
    cursor: default;
}

/* Enable heatmap squares for tooltips but prevent clicks via JavaScript */
.is-live-preview .cm-embed-block.cm-callout .heatmap-square,
.is-live-preview .cm-embed-block.cm-callout .heatmap-content > div > div {
    pointer-events: auto !important;
    cursor: help !important;
}

/* Enable year filter buttons */
.is-live-preview .cm-embed-block.cm-callout .year-filter-btn {
    pointer-events: auto !important;
    cursor: pointer !important;
}

/* === SELECTIVE BUTTON ENABLING === */
/* Disable buttons by default */
.is-live-preview .cm-embed-block.cm-callout button {
    pointer-events: none !important;
}

/* Re-enable specific interactive buttons */
.is-live-preview .cm-embed-block.cm-callout button.year-filter-btn,
.is-live-preview .cm-embed-block.cm-callout .year-filter-btn {
    pointer-events: auto !important;
    cursor: pointer !important;
}

/* === INTERACTIVE ELEMENTS === */
/* Enable elements with data attributes for interactions */
.is-live-preview .cm-embed-block.cm-callout [data-year] {
    pointer-events: auto !important;
    cursor: pointer !important;
}

/* Enable heatmap content container */
.is-live-preview .cm-embed-block.cm-callout .heatmap-content,
.is-live-preview .cm-embed-block.cm-callout .heatmap-content * {
    pointer-events: auto !important;
}

/* === TOOLTIPS === */
/* Ensure tooltips work properly on interactive elements */
.is-live-preview .cm-embed-block.cm-callout [title]:hover {
    position: relative;
}

/* === DEBUG HELPERS === */
/* Uncomment these to debug which elements are clickable */
/*
.is-live-preview .cm-embed-block.cm-callout * {
    border: 1px solid rgba(255, 0, 0, 0.2) !important;
}

.is-live-preview .cm-embed-block.cm-callout *[style*="pointer-events: none"] {
    border: 1px solid rgba(0, 255, 0, 0.3) !important;
}

.is-live-preview .cm-embed-block.cm-callout *[style*="pointer-events: auto"] {
    border: 1px solid rgba(0, 0, 255, 0.3) !important;
}
*/

/* === FINAL SAFETY NET === */
/* Disable pointer events on any remaining problematic elements */
.is-live-preview .cm-embed-block.cm-callout div:not(.year-filter-btn):not(.callout-title),
.is-live-preview .cm-embed-block.cm-callout span:not(.tag):not(.internal-link),
.is-live-preview .cm-embed-block.cm-callout p {
    pointer-events: none !important;
}

I also use it together with some dataviewjs with interactive buttons and elments, which is why the code probably became much longer than what it needs to be for plain text. But maybe one of you is able to dissect the essential elements of it. Have fun with it.

1 Like

oh interesting! i’ll give this a shot later

Some things weren’t right with the last css, this updated version should actually do everything it promises. All callouts are blocked from being clicked, links work, the edit button works, dataviewjs elements can still be clicked if necessary.

I’m sure there is still a lot of junk code for many, so in case someone is better at reading out the relevant parts, I think this would do the comunity a great favor.

/* === OBSIDIAN CALLOUT CLICK PREVENTION - SIMPLE & UNIVERSAL === */

/* Disable clicks on ALL callout backgrounds */
.is-live-preview .cm-embed-block.cm-callout {
    pointer-events: none !important;
}

/* === EDIT BUTTON AND CALLOUT CONTROLS === */
/* Enable edit button and callout controls */
.is-live-preview .cm-embed-block.cm-callout .edit-block-button,
.is-live-preview .cm-embed-block.cm-callout .cm-edit-block-button,
.is-live-preview .cm-embed-block.cm-callout .callout-fold,
.is-live-preview .cm-embed-block.cm-callout .callout-title .clickable-icon,
.is-live-preview .cm-embed-block.cm-callout .callout-title .clickable-icon[aria-label*="Edit"] {
    pointer-events: auto !important;
    cursor: pointer !important;
}

/* Enable callout title for edit button access */
.is-live-preview .cm-embed-block.cm-callout .callout-title {
    pointer-events: auto !important;
}

/* Disable title text to prevent accidental edit mode */
.is-live-preview .cm-embed-block.cm-callout .callout-title .callout-title-inner {
    pointer-events: none !important;
}

/* === BASIC INTERACTIVE ELEMENTS === */
/* Enable links */
.is-live-preview .cm-embed-block.cm-callout a,
.is-live-preview .cm-embed-block.cm-callout .internal-link,
.is-live-preview .cm-embed-block.cm-callout .external-link {
    pointer-events: auto !important;
    cursor: pointer !important;
}

/* Enable form elements */
.is-live-preview .cm-embed-block.cm-callout input,
.is-live-preview .cm-embed-block.cm-callout select,
.is-live-preview .cm-embed-block.cm-callout textarea,
.is-live-preview .cm-embed-block.cm-callout button {
    pointer-events: auto !important;
    cursor: pointer !important;
}

/* Enable checkboxes specifically */
.is-live-preview .cm-embed-block.cm-callout input[type="checkbox"] {
    pointer-events: auto !important;
    cursor: pointer !important;
}

/* === DATAVIEW ELEMENTS === */
/* Enable dataview rendered content and tables */
.is-live-preview .cm-embed-block.cm-callout .dataview-result-list-root-node,
.is-live-preview .cm-embed-block.cm-callout .dataview-result-list-root-node *,
.is-live-preview .cm-embed-block.cm-callout table,
.is-live-preview .cm-embed-block.cm-callout table * {
    pointer-events: auto !important;
}

/* Re-enable links within dataview */
.is-live-preview .cm-embed-block.cm-callout .dataview a,
.is-live-preview .cm-embed-block.cm-callout .dataview .internal-link,
.is-live-preview .cm-embed-block.cm-callout .dataview .external-link {
    pointer-events: auto !important;
    cursor: pointer !important;
}

/* === CHARTS AND VISUALIZATIONS === */
/* Enable canvas and SVG elements */
.is-live-preview .cm-embed-block.cm-callout canvas,
.is-live-preview .cm-embed-block.cm-callout svg {
    pointer-events: auto !important;
    cursor: default !important;
}

/* === MEDICATION HEATMAP === */
/* Enable heatmap container and all children */
.is-live-preview .cm-embed-block.cm-callout [id^="medication-heatmap"],
.is-live-preview .cm-embed-block.cm-callout [id^="medication-heatmap"] *,
.is-live-preview .cm-embed-block.cm-callout [id*="medication-heatmap"],
.is-live-preview .cm-embed-block.cm-callout [id*="medication-heatmap"] * {
    pointer-events: auto !important;
}

/* Enable heatmap squares for tooltips */
.is-live-preview .cm-embed-block.cm-callout .heatmap-square {
    pointer-events: auto !important;
    cursor: help !important;
}

/* Enable year filter buttons */
.is-live-preview .cm-embed-block.cm-callout .year-filter-btn {
    pointer-events: auto !important;
    cursor: pointer !important;
}

/* === CALENDAR AND DATE PICKERS === */
/* Enable Flatpickr calendar */
.is-live-preview .cm-embed-block.cm-callout .flatpickr-calendar,
.is-live-preview .cm-embed-block.cm-callout .flatpickr-calendar * {
    pointer-events: auto !important;
    cursor: pointer !important;
}

/* === GENERAL INTERACTIVE ELEMENTS === */
/* Enable common interactive elements */
.is-live-preview .cm-embed-block.cm-callout .clickable,
.is-live-preview .cm-embed-block.cm-callout [onclick],
.is-live-preview .cm-embed-block.cm-callout [role="button"],
.is-live-preview .cm-embed-block.cm-callout .interactive,
.is-live-preview .cm-embed-block.cm-callout [data-year],
.is-live-preview .cm-embed-block.cm-callout [data-toggle],
.is-live-preview .cm-embed-block.cm-callout [data-action] {
    pointer-events: auto !important;
    cursor: pointer !important;
}

/* === TOOLBAR ELEMENTS === */
/* Enable toolbar containers and buttons */
.is-live-preview .cm-embed-block.cm-callout .cg-note-toolbar-callout {
    pointer-events: auto !important;
}

.is-live-preview .cm-embed-block.cm-callout .cg-note-toolbar-callout button,
.is-live-preview .cm-embed-block.cm-callout .cg-note-toolbar-callout .clickable-icon {
    pointer-events: auto !important;
    cursor: pointer !important;
}

/* === CUSTOM CHART CONTAINERS === */
/* Enable any custom chart containers */
.is-live-preview .cm-embed-block.cm-callout .my-graph-container,
.is-live-preview .cm-embed-block.cm-callout .my-graph-container *,
.is-live-preview .cm-embed-block.cm-callout .chart-container,
.is-live-preview .cm-embed-block.cm-callout .chart-container * {
    pointer-events: auto !important;
}

/* === TEXT SELECTION === */
/* Disable text selection on callout content to prevent accidental selection */
.is-live-preview .cm-embed-block.cm-callout .callout-content {
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

/* Re-enable text selection for specific elements */
.is-live-preview .cm-embed-block.cm-callout .callout-content table,
.is-live-preview .cm-embed-block.cm-callout .callout-content pre,
.is-live-preview .cm-embed-block.cm-callout .callout-content code,
.is-live-preview .cm-embed-block.cm-callout .callout-content input[type="text"],
.is-live-preview .cm-embed-block.cm-callout .callout-content textarea {
    -webkit-user-select: text;
    -moz-user-select: text;
    -ms-user-select: text;
    user-select: text;
}

I use this on all callouts, but also because of things like these, in case someone was interested why there are so many lines about dataview and charts in it.

1 Like

initial test looks good! if I tinker with it I’ll report back with findings. Thanks!

The new Bases introduced in version 1.9 have the same issue. If embedded inside a callout, clicking on any of the fields to change properties edits the callout instead.

I tried this snippet, adding an exception for Bases, but I wasn’t able to fix the issue.

something fun I discovered is if I use a dataviewjs block to render a callout, the clicking behaves the way I want it to