Tikz: node in margin right to other node interacts with tcbtheorem

I am currently trying to use @GonzaloMedina's answer to create a framed environment for a margin note

Consider the following simplified code:

\documentclass[oneside]{book}
\usepackage[framemethod=tikz]{mdframed}
\usepackage{tikzpagenodes}
\usepackage{lipsum}

\newcounter{mycaution}
\newcommand\tikzmark[1]{%
\tikz[remember picture,overlay]
\node[inner xsep=0pt,outer sep=0pt] (#1) {};
}

\newcommand{\caution}{
\stepcounter{mycaution}
\tikzmark{\themycaution}%
\begin{tikzpicture}[remember picture,overlay]
\node[draw=red,anchor=west,xshift=\marginparsep,yshift=0pt]
(mybox\themycaution)
at ([yshift=3pt]current page text area.east|-\themycaution)
{\parbox{\marginparwidth}{Some text}};
\end{tikzpicture}
}

\begin{document}

\lipsum[1]
Some random text\caution{}
\lipsum[2-5]
\end{document}


The command \caution does what I expect: it produces a red box in the right margin, right to the text before the call of \caution.

Now, assume I want to do the same thing, but that \caution is called inside a tcbtheorem environnment. For example:

\documentclass[oneside]{book}
\usepackage[framemethod=tikz]{mdframed}
\usepackage{tikzpagenodes}
\usepackage[most]{tcolorbox}
\tcbuselibrary{theorems}
\usepackage{lipsum}

\newcounter{mycaution}
\newcommand\tikzmark[1]{%
\tikz[remember picture,overlay]
\node[inner xsep=0pt,outer sep=0pt] (#1) {};
}

\newcommand{\caution}{
\stepcounter{mycaution}
\tikzmark{\themycaution}%
\begin{tikzpicture}[remember picture,overlay]
\node[draw=red,anchor=west,xshift=\marginparsep,yshift=0pt]
(mybox\themycaution)
at ([yshift=3pt]current page text area.east|-\themycaution)
{\parbox{\marginparwidth}{Some text}};
\end{tikzpicture}
}

\newtcbtheorem{theo}{Theorem}{theorem style=plain}{th}

\begin{document}
\begin{theo}{}{}

Some random text\caution{}
\lipsum[1]
\end{theo}

\lipsum[1]
Some random text\caution{}

\end{document}


Then the output is the following:

I am not used to TikZ, but what I understand is that:

• the effect of tikzmark is to place a node a the exact place where it is called, and the coordinates of this node can be used later,
• the remember picture, overlay option of tikzpicture allow to use these coordinates.

However, a tcolorbox (and hence a tcbtheorem) is drawn using TikZ, so that these coordinates are kind of lost (except that the problem only comes from the x-coordinate, the y-coordinate seems to be good).

It seems that tikzmark gives a name (the value of mycaution) to the node it creates, but that we do not really use this name later, assuming that TikZ has not been used between the call of \tikzmark and the drawing of the box. However, I have no idea about how I can use this name to specify that my box should be in the margin right to this node...

Can anyone provide a solution (or better: an explanation of what really happens here)?

The problem is that the tcolorbox changes \textwidth which is used bt the tikzpagenodes package to internally calculate the position for the page nodes it defines.

You can see this using

\documentclass[oneside]{book}
\usepackage{tikzpagenodes}
\usepackage[most]{tcolorbox}

\newtcbtheorem{theo}{Theorem}{theorem style=plain}{th}

\begin{document}

\the\textwidth
\begin{theo}{}{}
\the\textwidth
\end{theo}

\end{document}


which gives:

so inside the tcolorbox the current page text area.east node is to the left of where it should really be.

One way to prevent this is to use the correct anchors which we retrieve before the theo environment with the help of the etoolbox package and redefine \caution to use the corrected anchor:

\documentclass[oneside]{book}
\usepackage[framemethod=tikz]{mdframed}
\usepackage{tikzpagenodes}
\usepackage[most]{tcolorbox}
\tcbuselibrary{theorems}
\usepackage{lipsum}
\usepackage{etoolbox}
\usetikzlibrary{fit}

\newcounter{mycaution}
\newcommand\tikzmark[1]{%
\tikz[remember picture,overlay]
\node[inner xsep=0pt,outer sep=0pt] (#1) {};
}

\newcommand{\caution}{
\stepcounter{mycaution}%
\tikzmark{\themycaution}%
\begin{tikzpicture}[remember picture,overlay]
\node[draw=red,anchor=west,xshift=\marginparsep,yshift=0pt]
(mybox\themycaution)
at ([yshift=3pt]current page text area.east|-\themycaution)
{\parbox{\marginparwidth}{Some text}};
\end{tikzpicture}
}

\newtcbtheorem{theo}{Theorem}{theorem style=plain}{th}

\BeforeBeginEnvironment{theo}{%
\tikz[remember picture,overlay]
\node[fit=(current page text area),line width=0,inner sep=0,name=correct current page text area]{};
\renewcommand{\caution}{
\stepcounter{mycaution}%
\tikzmark{\themycaution}%
\begin{tikzpicture}[remember picture,overlay]
\node[draw=red,anchor=west,xshift=\marginparsep,yshift=0pt]
(mybox\themycaution)
at ([yshift=3pt]correct current page text area.east|-\themycaution)
{\parbox{\marginparwidth}{Some text}};
\end{tikzpicture}%
}%
}

\begin{document}

\begin{theo}{}{}
Some random text\caution{}
\lipsum[1]
\end{theo}

\lipsum[1]
Some random text\caution{}

\end{document}


The result:

• nice answer, thank you for the clear explanation ! Your knowledge of TikZ is really impressive. However, do I need to manually set corrected page text area every time I am about to use caution inside of thm, or do you think of a way to do this automatically ? – mvienney Jul 29 '15 at 18:14
• @mvienney Thak you for your kind words. I updated my answer with an automatic approach. – Gonzalo Medina Jul 29 '15 at 18:49
• thanks again ! I had tried something like your solution, but it was not working on my complete document... took me some time to figure out it was an externalization problem (if anyone is someday interested in this post : you MUST disable externalization before setting the node called correct current .... Seems quite obvious, but still...). However, I still think that you're some kind of TikZ sorcerer :-) – mvienney Jul 29 '15 at 21:58