<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>TIL | Carlos Eduardo Ferreira</title>
    <link>https://carloseduardodev.com/til</link>
    <description>Notas curtas e diretas sobre descobertas do dia a dia: comandos, detalhes de API e armadilhas que não rendem um artigo inteiro.</description>
    <language>pt-BR</language>
    <managingEditor>contatocarloseduardofe@gmail.com (Carlos Eduardo Ferreira)</managingEditor>
    <lastBuildDate>Sun, 04 Oct 2026 12:00:00 GMT</lastBuildDate>
    <atom:link href="https://carloseduardodev.com/til/rss.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>Âncoras quebram quando o scroll não é da janela</title>
      <link>https://carloseduardodev.com/til/scroll-margin-top-depende-do-container-de-scroll</link>
      <guid isPermaLink="true">https://carloseduardodev.com/til/scroll-margin-top-depende-do-container-de-scroll</guid>
      <pubDate>Sun, 04 Oct 2026 12:00:00 GMT</pubDate>
      <description>Um layout com altura fixa e overflow no main faz links de âncora pararem atrás do header fixo, e o scroll-margin-top precisa ser aplicado considerando esse container.</description>
      <category>CSS</category>
      <category>Layout</category>
      <content:encoded><![CDATA[<p>Meu layout tem altura de tela cheia, com o scroll acontecendo dentro do <code>main</code> e
não na janela:</p>
<pre class="shiki shiki-themes github-light github-dark-dimmed" style="--shiki-light:#24292e;--shiki-dark:#adbac7;--shiki-light-bg:#fff;--shiki-dark-bg:#22272e" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">&lt;</span><span style="--shiki-light:#22863A;--shiki-dark:#8DDB8C">div</span><span style="--shiki-light:#6F42C1;--shiki-dark:#6CB6FF"> className</span><span style="--shiki-light:#D73A49;--shiki-dark:#F47067">=</span><span style="--shiki-light:#032F62;--shiki-dark:#96D0FF">&quot;flex h-screen overflow-hidden&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">&gt;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">  &lt;</span><span style="--shiki-light:#005CC5;--shiki-dark:#8DDB8C">AppSidebar</span><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7"> /&gt;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">  &lt;</span><span style="--shiki-light:#22863A;--shiki-dark:#8DDB8C">main</span><span style="--shiki-light:#6F42C1;--shiki-dark:#6CB6FF"> className</span><span style="--shiki-light:#D73A49;--shiki-dark:#F47067">=</span><span style="--shiki-light:#032F62;--shiki-dark:#96D0FF">&quot;flex-1 overflow-y-auto&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">&gt;</span><span style="--shiki-light:#24292E;--shiki-dark:#F47067">{</span><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">children</span><span style="--shiki-light:#24292E;--shiki-dark:#F47067">}</span><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">&lt;/</span><span style="--shiki-light:#22863A;--shiki-dark:#8DDB8C">main</span><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">&gt;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">&lt;/</span><span style="--shiki-light:#22863A;--shiki-dark:#8DDB8C">div</span><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">&gt;</span></span></code></pre>
<p>Isso dá uma sensação de aplicação, e quebra duas coisas que normalmente
funcionam de graça.</p>
<h2 id="títulos-param-atrás-do-header">Títulos param atrás do header<a href="#títulos-param-atrás-do-header" class="heading-anchor" tabindex="-1" aria-hidden="true"><span>#</span></a></h2>
<p>Clicar em um link de âncora rola o título até o topo do container, exatamente
onde fica o header fixo de 4rem. O título some atrás dele.</p>
<p><code>scroll-margin-top</code> resolve, e o valor precisa considerar a altura do header:</p>
<pre class="shiki shiki-themes github-light github-dark-dimmed" style="--shiki-light:#24292e;--shiki-dark:#adbac7;--shiki-light-bg:#fff;--shiki-dark-bg:#22272e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#6CB6FF">.prose</span><span style="--shiki-light:#22863A;--shiki-dark:#8DDB8C"> h2</span><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">,</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#6CB6FF">.prose</span><span style="--shiki-light:#22863A;--shiki-dark:#8DDB8C"> h3</span><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7"> {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#6CB6FF">  scroll-margin-top</span><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#6CB6FF">5</span><span style="--shiki-light:#D73A49;--shiki-dark:#F47067">rem</span><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">}</span></span></code></pre>
<p>A propriedade vai no <strong>elemento de destino</strong>, não no container. Isso é
contraintuitivo na primeira vez: você quer mexer no que rola, mas quem precisa do
recuo é quem chega.</p>
<h2 id="intersectionobserver-observa-a-janela-por-padrão">IntersectionObserver observa a janela por padrão<a href="#intersectionobserver-observa-a-janela-por-padrão" class="heading-anchor" tabindex="-1" aria-hidden="true"><span>#</span></a></h2>
<p>O segundo tropeço: para destacar a seção ativa em um índice lateral, o
<code>IntersectionObserver</code> precisa saber qual é o container de scroll. O padrão é o
viewport, e com o scroll no <code>main</code> nenhum título nunca entra ou sai de vista do
ponto de vista dele.</p>
<pre class="shiki shiki-themes github-light github-dark-dimmed" style="--shiki-light:#24292e;--shiki-dark:#adbac7;--shiki-light-bg:#fff;--shiki-dark-bg:#22272e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F47067">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#6CB6FF"> observer</span><span style="--shiki-light:#D73A49;--shiki-dark:#F47067"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F47067"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#DCBDFB"> IntersectionObserver</span><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">(callback, {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">  root: document.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#DCBDFB">querySelector</span><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">(</span><span style="--shiki-light:#032F62;--shiki-dark:#96D0FF">&quot;main&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">  rootMargin: </span><span style="--shiki-light:#032F62;--shiki-dark:#96D0FF">&quot;-80px 0px -70% 0px&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">});</span></span></code></pre>
<p>Sem o <code>root</code>, o callback dispara uma vez no carregamento e nunca mais.</p>]]></content:encoded>
    </item>
    <item>
      <title>String.replace interpreta $ mesmo quando o padrão é uma string</title>
      <link>https://carloseduardodev.com/til/string-replace-interpreta-dollar-na-substituicao</link>
      <guid isPermaLink="true">https://carloseduardodev.com/til/string-replace-interpreta-dollar-na-substituicao</guid>
      <pubDate>Fri, 02 Oct 2026 12:00:00 GMT</pubDate>
      <description>Injetar HTML renderizado com replace pode corromper a saída silenciosamente, porque a string de substituição ainda passa pelo processamento de padrões.</description>
      <category>JavaScript</category>
      <category>Armadilhas</category>
      <content:encoded><![CDATA[<p>Eu estava injetando HTML pré-renderizado em um template:</p>
<pre class="shiki shiki-themes github-light github-dark-dimmed" style="--shiki-light:#24292e;--shiki-dark:#adbac7;--shiki-light-bg:#fff;--shiki-dark-bg:#22272e" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">template.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#DCBDFB">replace</span><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">(</span><span style="--shiki-light:#032F62;--shiki-dark:#96D0FF">&quot;&lt;!--app-html--&gt;&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">, html);</span></span></code></pre>
<p>Funciona na maioria dos casos, e falha de um jeito difícil de rastrear quando o
HTML contém certos caracteres.</p>
<p>O detalhe: mesmo quando o <strong>padrão</strong> é uma string comum, a <strong>substituição</strong> passa
pelo processamento de padrões. Sequências como <code>$&amp;</code>, <code>$1</code>, <code>$`</code> e <code>$&#x27;</code> têm
significado especial:</p>
<pre class="shiki shiki-themes github-light github-dark-dimmed" style="--shiki-light:#24292e;--shiki-dark:#adbac7;--shiki-light-bg:#fff;--shiki-dark-bg:#22272e" tabindex="0"><code><span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#96D0FF">&quot;alvo&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#DCBDFB">replace</span><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">(</span><span style="--shiki-light:#032F62;--shiki-dark:#96D0FF">&quot;alvo&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">, </span><span style="--shiki-light:#032F62;--shiki-dark:#96D0FF">&quot;a$&amp;b&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">);</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#768390">// &quot;aalvob&quot;  -- e nao &quot;a$&amp;b&quot;</span></span></code></pre>
<p>Em HTML real isso aparece em lugar nenhum óbvio, até alguém escrever um artigo
sobre expressões regulares e <code>$&amp;</code> ir junto no texto. Aí um pedaço do documento
simplesmente se duplica ou desaparece.</p>
<p>A correção é usar uma função como substituição, porque o retorno dela é inserido
literalmente:</p>
<pre class="shiki shiki-themes github-light github-dark-dimmed" style="--shiki-light:#24292e;--shiki-dark:#adbac7;--shiki-light-bg:#fff;--shiki-dark-bg:#22272e" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">template.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#DCBDFB">replace</span><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">(</span><span style="--shiki-light:#032F62;--shiki-dark:#96D0FF">&quot;&lt;!--app-html--&gt;&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7">, () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F47067">=&gt;</span><span style="--shiki-light:#24292E;--shiki-dark:#ADBAC7"> html);</span></span></code></pre>
<p>Vale para qualquer conteúdo que você não controla: HTML renderizado, texto de
usuário, JSON serializado. Se o valor vem de fora, use função.</p>]]></content:encoded>
    </item>
  </channel>
</rss>
