My words will become her inner voice

| parenting

Trawling through blogs to add to my feed reader, I came across a number of posts related to the February 2025 IndieWeb Carnival theme of affirmations. It nudged me to think about the words I rub into A+'s brain with repetition.

Once a year or so, A+'s virtual school teachers typically play a video about affirmations as part of the social-emotional learning aspect of the curriculum.1 The perky narrator exhorts the listeners to repeat the affirmations after her. A+ doesn't. I don't know if many of the other kids do.

The words I use with A+ are more powerful, so I want to be thoughtful about them. There's this idea floating around: the way we talk to our children becomes their inner voice.2

Here's what I find myself repeating:

  • "You worked hard on that." I'm not 100% clear on the results of the meta-analyses of growth mindset research, but it makes sense to me to focus on acknowledging her effort rather than things that might be considered fixed characteristics. It's a good contrast to the "You're so smart!" that she might get from people impressed by her skills at solving Rubik's cubes or her enjoyment of math. She likes it when I see what she's doing, so I also add specific descriptions, and I extend it with a long-term perspective. Today she excitedly told me about how she worked together with a classmate on their literacy groupwork, so I echoed that back to her and connected it to her growth over time. I also often say "You did that!" and flesh it out with details.
  • "You got this." Because it's important for her to feel my belief and trust in her, and to enjoy confidence and self-efficacy. She's been doing well with increased personal responsibility for things like homework. Sometimes I say "We got this" instead, when we're working together.
  • "You know the way." Because it's extra fun (and extra effective) to sing the things I want her to remember, and because often she already knows the next thing she needs to do and this is a fun way of sending her off. This one is from the We Know the Way song from Moana. I like how I can use snippets of Disney musicals and bring in all the emotional storytelling connected to them. I occasionally touch on another line from that song: "We set a course to find a brand new island everywhere we roam." She's going to have to find a brand new way. She can do it. We're wayfinders.
  • "I got you." Because sometimes she stumbles and falls, and that's okay. We're here. Sometimes all she needs is a snuggle. Sometimes she needs a bandage or an energy bar or an emergency frozen treat. Sometimes she needs an extra pair of hands or someone to brainstorm with. We can figure things out together.
  • "You're wonderful." Because it's good for her to know I'm glad she exists, in a way that focuses on her wonderfulness rather than my act of loving her. Along these lines, I also frequently say, "You're awesome!"
  • "It's your experiment!" Because trying things out will help her learn, and this reminds me to back off and let her decide.

Over dinner, I mentioned this list of things I often say to A+. She wanted me to add these ones:

  • "I appreciate you, child."
  • "I love you."
  • "I'm going to pounce on you." Which is usually interrupted by her pouncing on me. This amuses her greatly. I like this playful way to offer a hug.

It is easiest to say these things when we're both well-rested and in good moods, so part of my job is to manage myself so that I can be in that state as often as possible.

A+'s beginning to echo these words back to us. She tells me what she'd like to use her allowance to experiment with. Yesterday at dinner, she told me, "You're awesome!" in the same way that I often tell her. Sometimes, as I send her off on her next task, she sings, "We know the way."

One night, as I was tucking A+ in, she asked for snuggles. I burrowed under the blankets and said, "I got you." She said, "You always do."

Footnotes

1

A1.3 Positive Motivation and Perseverance in Health and Physical Education (2019)

2

Hard to pin down a specific attribution. Goodreads has a page attributing this quote to Peggy O'Mara, but I can't find a citation. There are also plenty of variants on the Web, like this "The way we speak to our children becomes their inner voice" article from Kurtz Psychology. Anyway, handwaving this as a pithy concept I didn't come up with.

View org source for this post

Cases of mangoes, coolers of freezies

| parenting, life

Assumed audience:

  • Future me, when mango freezies are a distant memory
  • Maybe other parents who might consider splurging on fruits
2025-04-08-cases-of-mangoes.jpg

We have two cases of mangoes on our kitchen counter: the last case of Ataulfo mangoes W- could find at the Nations supermarket a short bike-ride away from the house (slightly underripe at purchase; it's been a few days, so now A+ says they're perfect), and a case of Tommy Atkins mangoes that are greener and tougher. He had meant to buy Hadens, but accidentally picked the Tommys up instead. That's okay, I said. I can turn them into mango shakes.

Wikipedia describes the Tommys as:

Although generally not considered to be the best in terms of sweetness and flavor, it is valued for its very long shelf life and tolerance of handling and transportation with little or no bruising or degradation.

Thomas Atkins submitted the fruit to the variety committee of the Florida Mango Forum multiple times during the 1950s, which rejected it due to its unremarkable eating qualities and considerable fiber in the flesh.

2025-04-08-mango-turtle.jpg

A+ has definitely developed mango preferences. Like me, she likes the smooth, sweet creaminess of Ataulfo and other Philippine-type mangoes. She's the one who regularly checks the mangoes for ripeness and reports on their status, letting us know as soon as they're soft. After a good meal, she often prepares a mango for herself, using a paring knife to skim it close to the seed. Sometimes she cuts a criss-cross grid and flips it into a turtle, or scoops out the insides for chopstick practice. She's gotten much better at getting most of the mango out; there's usually very little for me to scrape off the rest of the skins. Without prompting, she remembers to wash her hands before and after. One evening, watching her deftly cut her mango, I said, "People pay good money to send kids to cooking classes so they can pick up knife skills. Could buy a lot of mangoes for that money." We had fun joking about the short-term and long-term benefits.

I grew up eating mangoes in the Philippines. Sometimes they were on the breakfast table. Sometimes I had them at merienda (afternoon snack). Mangoes were either yellow, kidney-shaped, soft, and sweet, or the tart green mangoes that were also delicious in a different way, either straight-up or with salt or with bagoong (fermented fish or shrimp paste). (It took me a while to appreciate bagoong, but eventually I got the hang of it.) Green mango shakes were also a treat.

2025-04-08-mango-treats.jpg

After I moved to Canada in 2005, I went a long time without regularly buying mangoes. Still mentally converting costs to Philippine pesos, I balked at the expense of individual mangoes. The supermarket rarely sold good-looking Ataulfo mangoes, mostly just other mango cultivars that were more fibrous. If they did have the yellow kidney-shaped mangoes of my memory, the ones sold individually tended to be wrinkly and sad. A case of mangoes felt like overkill for just me, and I never quite got around to seeing if any of my friends wanted to, I don't know, rotate mango buying.

I think we only started buying cases of mangoes last year or the year before that. Now we're more comfortable knowing that even if we buy cases one after the other, we'll eat them before they go bad. We'll never say, "I'm all mangoed out." (And if we ever do get to that point, I can just dehydrate whichever mangoes are left.) It is definitely a frill and we're lucky to be able to enjoy them.

2025-04-08-blender.jpg

When it's warm enough for the kids to complain of the heat, we tend to ramp up our mango consumption even more. For the past two years, I've been bringing a cooler of frozen treats to A+'s park playdates. It started as a way to fend off temptation from the ice cream trucks that like to prowl around parks, and as a reaction to the ridiculousness of retail/wholesale pricing when it comes to summer refreshments. My days in A+'s sphere of friends are numbered, so I may as well make the most of it. It's a splurge, but time-limited. She will probably not invite me to lurk in the background with a cooler of popsicles when she's 18 years old. So yes to all the things, for now: home-made mango freezies; strawberries and peaches from the farmers market when they're in season; freezies made from pick-your-own strawberries when we make it out to a farm. (Child labour!) Raspberry freezies are sometimes too intense or too seedy for the kids. A+ is not a fan of cantaloupe freezies, but I like them. For the watermelon freezes, we're a bit divided. I like to add a little bit of sugar to the watermelon if it seems like it needs it. A+ takes pride in not adding any sugar to home-made freezies, so whenever she wants to be in charge of making them (which is almost always), I let her go with whatever she wants. She likes to use the mini-watermelons and add a splash of lemon juice. It always tastes refreshing.

Sometimes we bring Chapman's ice lollies (the kids prefer the single-flavour ones) or other store-bought treats. Sometimes A+ proudly insists on paying for these herself; early experiences of prosocial spending, hooray! When the fruits are in season, I prefer to make home-made freezies. We don't follow any recipes. We just prepare the fruits and put them into the blender.

2025-04-08-popsicle-bag.jpg

I use disposable popsicle bags from AliExpress since I haven't found a local source I like, and have determined that:

  • Zipper seals are nice for filling, but difficult to open. The best bags have a little notch for tearing under the seal, but this is hard to see in product photos. I often bring scissors if the bags don't have notches.
  • 22cm x 5cm: just right
  • 28cm x 5.5cm: too much

We have a couple of reusable freezie molds that we sometimes use. I save those for A+ and me because I know they'll find their way back to the cooler.

I used to walk to the playdates with A+ in a stroller (she still fits in the Thule Chariot Cheetah XT, even at 9 years old), with the cooler balanced on top of it. It took me 45 minutes to an hour to walk to her usual park playdates, but the freezies were fine if I packed the cooler with lots of ice packs. With the cargo bike, I can get to the playdates in 15 minutes or so, which means there's plenty of time for A+ to play and get warm before she decides it's time for a freezie break, and the freezies are all still nicely cold.

2025-04-08-friends.jpg

A+'s favourite friends get first dibs by virtue of proximity when she decides it's popsicle time (after A+, of course, who gets first pick). It feels quite satisfying when A+'s friends sidle up and ask very politely if there happen to be any more of those mango popsicles. Then we extend the selection to everyone else in the playgroup, and then, when everyone's sorted out, the occasional brave soul who wanders up to the strange woman handing frozen treats out to kids. I try to make eye contact with their grown-up first to check if it's okay. Sometimes when I'm distracted, I ask the new kids if their grown-up is okay with it, but I get the feeling that their quick nod might not be entirely reliable as it tends to be done with their eyes fixed on the prize. Gotta find their actual grown-up. I know A+ likes to go back for seconds or thirds on really hot days, so sometimes I keep a special stash for her in a nylon drawstring bag in the cooler. Sometimes I have one too.

2025-04-08-mango-heart.jpg

I'm Filipino. Part of my love language is food. Taste can anchor memories, and I hope these are part of her core experiences of childhood. I want these to be part of my memories of her. That's worth the mangoes.

View org source for this post

2025-04-07 Emacs news

| emacs, emacs-news

Links from reddit.com/r/emacs, r/orgmode, r/spacemacs, r/planetemacs, Mastodon #emacs, Bluesky #emacs, Hacker News, lobste.rs, programming.dev, lemmy.world, lemmy.ml, planet.emacslife.com, YouTube, the Emacs NEWS file, Emacs Calendar, and emacs-devel. Thanks to Andrés Ramírez for emacs-devel links. Do you have an Emacs-related link or announcement? Please e-mail me at sacha@sachachua.com. Thank you!

View org source for this post

Week ending April 4, 2025: blog tweaks

| review, weekly
  • Thanks to Pictionary, we caught up with all the illustrations that A+ needed to do for her homework.
  • I tweaked my blog navigation and headings. I also exported the comments from Disqus and took it off my site.
  • I got rid of some clutter.

Blog posts

Sketches

Toots

  • The Experimental Parent | Psychology Today (toot) I saw this snippet in a 2016 interview in Psychology Today with T. Berry Brazelton:

    “BB: I've just finished writing a book, The Final Touchpoint I'd like to get that out there. There are better and worse ways to handle our aging, our denial of it, our acceptance, and—as Erik Erikson put it—our being generative, to produce as much as we can while we can. I'm 98 but I'm still trying to be generative.”

    He died two years after the interview and I don't think The Final Touchpoint has been published, but it might be interesting to find similar books.

  • Memexes, mountain lakes, and the serendipity of old ideas (Interconnected) (toot) Hmm, an On This Day RSS feed might be worth writing a tiny script that I can add to a crontab.

    “Naturally there's an On This Day web feed too so these posts appear in my newsreader each morning. Some personal serendipity to start the day.”

  • Oh Hello Ana - In defense of unpolished personal websites: (toot) On the value of legible source code for websites, especially personal ones:

    “Today's heavily optimized websites have largely killed the "view source" learning experience. The code is minified, bundled, and often incomprehensible to beginners trying to understand how things work.
    I got the ick from my own small optimisation. My personal website is small and it isn't an urgent service. It's hardly ever visited from a mobile phone. Maybe I shouldn't be using the little time I have to focus on that side of front-end development in this instance?
    But deep down, all I want for my personal website is to give back to the web. I want anyone, regardless of skill level, to inspect elements, understand the structure, and learn from readable code. And I am fully aware my code isn't perfect. It's old and there's a lot of room for improvement.”

    Found via Favourites of March 2025 | Brain Baking

  • The Surprising Richness of Correlations (toot) I like the way this post explains the math behind statistical analyses of correlations with clear words and hand-drawn graphs.
  • On homework: (toot) Homework experiments continue. So far, we have determined that homework is more likely to be done if the kiddo is on top of me (2 instances) or if she's dictating answers while eating lunch (1 instance) or playing Minecraft (1 instance).
Time
Category The other week % Last week % Diff % h/wk Diff h/wk
Personal 9.9 16.1 6.3 27.1 10.5
Discretionary - Productive 20.1 21.6 1.5 36.3 2.6
Business 0.8 0.6 -0.2 1.1 -0.3
Discretionary - Family 0.3 0.1 -0.2 0.1 -0.4
Discretionary - Play 1.6 1.1 -0.5 1.9 -0.8
Sleep 31.1 30.6 -0.5 51.4 -0.9
Unpaid work 4.7 3.6 -1.1 6.1 -1.8
A+ 31.5 26.2 -5.4 44.0 -9.0

More walking and just chilling out this week.

View org source for this post

Adding subheadings and sketches to my blog page navigation

Posted: - Modified: | 11ty, blogging

[2025-04-04 Fri]: Fixed link to onThisPage.cjs. Thanks to John Rakestraw for pointing it out!

Assumed audience:

  • My future self, when I'm trying to figure out where to change things if I want to implement something similar
  • People who like tweaking their blog's CSS, especially if they also use Org Mode or 11ty

Headings help us make sense of longer blog posts. Heading links are like signposts letting you know what's ahead and where you can take a shortcut to get to what you're interested in.

Headings are useful for me too. Sometimes I browse my blog and come across things I've completely forgotten writing about, so the headings can help me remember without having to reread long posts. If I use headings more often, I might be able to work with bigger chunks of thoughts. If I can work with bigger chunks of thoughts, then maybe I can think about more things that are hard to fit within the limits of my working memory. Making headings more navigable also means I might not have to worry about the tangents I go on and the number of different thoughts I try to smoosh together, if people can jump straight to the parts that sound relevant to them.

I particularly like the way Karthik uses a sticky table of contents for long blog posts like The Emacs Window Management Almanac | Karthinks. I also like the way the Read the Docs Sphinx Theme displays a nested table of contents on the left side on wide screens. I use org-html-themes to export Org Mode files with that theme when I want to be fancy, like my Emacs configuration (usually works, although sometimes my config is broken).

The last time I tinkered with my webpage margins, I put my "On this page" list on the left side and the blog post headings (if any) on the right side, mostly because it was easy to do. I just changed the margin and float attributes of the element with the subheadings. I'd like to clear up more space for potential sidenotes or doodles, though. This time, I experimented with nesting the blog navigation inside the "On this page" navigation on the left side.

2025-04-02_14-04-30.png
Figure 1: A screenshot of my blog showing the nested links

Here's how I did it:

Org Mode: where I start writing

I use Org Mode's table of contents directive to include a table of contents in blog posts. I wrap it inside a sticky-toc block to indicate when I want it to be part of the sticky table of contents on my blog. In Org Mode, the syntax looks like this:

#+begin_sticky-toc
#+TOC: headlines 2 local
#+end_sticky-toc

I put it in a yasnippet so that I don't have to remember it. I just type tocs (for TOC, sticky) and then press TAB to complete it.

11ty static site generator: on this page

After exporting individual HTML files from Org Mode, I turn them into my blog using the 11ty static site generator. To simplify my archive pages, I have an onThisPage shortcode which lists the posts on that page. I changed it to include the sticky-toc contents from the items' templateContent attributes.

const { JSDOM } = require('jsdom');

module.exports = function (eleventyConfig) {
  function formatPostLine(item, index) {
    let subtoc = '';
    if (item.templateContent?.match(/sticky-toc/)) {
      const doc = new JSDOM(item.templateContent).window.document;
      const sub = doc.querySelector('.sticky-toc ul, .sticky-toc div, .sticky-toc-after-scrolling div');
      if (sub) {
        if (sub.querySelector('.panzoom')) {
          console.log('remove panzoom');
          sub.querySelector('.panzoom').classList.remove('panzoom');  // don't do panzoom for now
        }
        sub.classList.remove('panzoom');
        subtoc = sub.outerHTML;
      }
    }
    return `<li><a class="toc-link" data-index="index${index}" href="${item.url}">${item.data.title}</a>${subtoc}</li>`;
  }
  eleventyConfig.addShortcode('onThisPage', function (list) {
    return `<nav class="on-this-page">
On this page:
<ul>
${list.map(formatPostLine).join("\n")}
</ul>
</nav>`;
  });
};

And then there's a bunch of CSS in assets/css/style.css:

CSS
/* tables of contents */
.on-this-page > ul > li > ul, .on-this-page > ul > li > div { display: none }

@media only screen and (width >= 95em) {
    html, body { overflow-x: unset; }

    .sticky-toc, .sticky-left, .sticky-right {
        font-size: var(--fs-sm);
        width: calc((100vw - var(--body-max-width) - 5rem)/2);
        position: sticky;
        max-height: calc(100vh - 2rem);
        overflow-y: auto;
        scroll-behavior: smooth;
        background: var(--modus-bg-main);
        top: 0;
        padding: 1rem;
    }

    article .sticky-toc {
        display: none
    }

    .single-post article .sticky-toc {
        display: block;
    }

    .sticky-toc, .sticky-left, .single-post article .sticky-toc {
        margin-left: calc((-100vw + var(--body-max-width))/2);
        float: left;
    }

    .sticky-right {
        margin-right: calc((-100vw + var(--body-max-width))/2);
        float: right;
    }

    /* Hide the TOCs for non-active posts, but only if JS is enabled */
    .js .on-this-page > ul > li > ul, .js .on-this-page > ul > li > div { display: none }
    .on-this-page > ul > li.post-active > ul, .on-this-page > ul > li.post-active > div { display: block }

    .active { background-color: var(--modus-bg-tab-bar) }

    .sticky-toc svg .active rect {
        fill: var(--modus-bg-tab-bar) !important;
        fill-opacity: 1 !important;
        mix-blend-mode: darken;
        stroke-dash-array: unset !important;
        stroke-width: 4px;
    }

    .link-to-nonsticky-toc {
        display: none
    }
}


I also have some Javascript to highlight the active post and show the subheadings for it in assets/js/misc.js.

Javascript
/* Table of contents */

function stickyTocAfterScrolling() {
  const elements = document.querySelectorAll('.single-post .sticky-toc-after-scrolling');
  let lastScroll = window.scrollY;

  elements.forEach(element => {
    const clone = element.cloneNode(true);
    clone.setAttribute('class', 'sticky-toc');
    clone.querySelector('.panzoom')?.classList.remove('panzoom');
    element.parentNode.insertBefore(clone, element.nextSibling);
  });

  const observer = new IntersectionObserver(
    (entries) => {
      const currentScroll = window.scrollY;
      const scrollingDown = currentScroll > lastScroll;
      lastScroll = currentScroll;

      entries.forEach(entry => {
        const element = entry.target;
        const clone = cloneMap.get(element);

        if (!entry.isIntersecting && scrollingDown) {
          clone.setAttribute('class', 'sticky-toc');
          clone.style.display = 'block';
        } else if (entry.isIntersecting && !scrollingDown) {
          element.style.visibility = 'visible';
          clone.style.display = 'none';
        }
      });
    },
    {
      root: null,
      threshold: 0,
      rootMargin: '-10px 0px 0px 0px'
    }
  );

  elements.forEach(element => {
    observer.observe(element);
  });

  window.addEventListener('resize', () => {
    elements.forEach(element => {
      const clone = cloneMap.get(element);
      if (clone.style.display != 'none') {
        // reset didn't seem to work
        svgPanZoom(clone.querySelector('svg')).destroy();
        addPanZoomToElement(clone.querySelector('svg'));
      }
    });
  }, { passive: true });
}

stickyTocAfterScrolling();

function scrollToActiveTocLink() {
  const activeLink = document.querySelector('.sticky-toc .active');
  const tocContainer = document.querySelector('.sticky-toc');
  if (!activeLink || !tocContainer) return;
  const tocRect = tocContainer.getBoundingClientRect();
  const linkRect = activeLink.getBoundingClientRect();
  if (linkRect.top < tocRect.top || linkRect.bottom > tocRect.bottom) {
    const scrollPosition = linkRect.top + tocContainer.scrollTop -
                          (tocRect.height / 2) + (linkRect.height / 2);
    tocContainer.scrollTo({
      top: scrollPosition,
      behavior: 'smooth'
    });
  }
}
function getVisibleArticle() {
  const viewportHeight = window.innerHeight || document.documentElement.clientHeight;
  return [...document.querySelectorAll('article')].find((article) => {
    const rect = article.getBoundingClientRect();
    const visibleTop = Math.max(0, rect.top);
    const visibleBottom = Math.min(viewportHeight, rect.bottom);
    const visibleHeight = Math.max(0, visibleBottom - visibleTop);
    return visibleHeight > 0; // find the first visible one
  });
}

function handleActiveTOCLink() {
  const updateActive = function(links, active) {
    const activeFragment = active.includes('#') ?
          active.substring(active.indexOf('#')) : '';
    links.forEach(link => {
      const href = link.getAttribute('href');
      if (href.includes(window.location.origin)) {
        link.classList.toggle('active', href == active)
      } else if (href.startsWith('#')) {
        link.classList.toggle('active', href == activeFragment);
      }
    });
  };
  const posts = document.querySelectorAll('.post');
  const tocLinks = document.querySelectorAll('.on-this-page .toc-link');
  const options = {
    root: null,
    rootMargin: '-20% 0px -70% 0px',
    threshold: 0
  };
  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        const id = entry.target.id;
        const link = document.querySelector(`.toc-link[data-index="${id}"]`);
        document.querySelectorAll('.sticky-toc .active').forEach((o) => o.classList.remove('active'));
        document.querySelectorAll('.post-active').forEach((o) => o.classList.remove('post-active'));
        if (link) {
          link.classList.add('active');
          const item = link.closest('li');
          item.classList.add('post-active');
        }
      }
    });
    scrollToActiveTocLink();
  }, options);
  posts.forEach((post) => { observer.observe(post); });

  const stickyTocLinks = document.querySelectorAll('article .sticky-toc a, .on-this-page a');
  const postTocObserver = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        const id = entry.target.id;
        const url = window.location.origin + window.location.pathname + '#' + id.replace(/^outline-container-/, '');
        updateActive(stickyTocLinks, url);
      }
    });
    scrollToActiveTocLink();
  }, options);

  document.querySelectorAll('article .sticky-toc, article .sticky-toc-after-scrolling').forEach((toc) => {
    const post = toc.closest('article');
    if (post) {
      post.querySelectorAll('.outline-2, .outline-3').forEach((section) => { postTocObserver.observe(section) });
    }
  });
  const visible = getVisibleArticle();
  const id = visible?.id;
  if (id) {
    const activeLink = document.querySelector(`.toc-link[data-index="${id}"]`);
    if (activeLink) {
      activeLink.classList.add('active');
      activeLink.closest('li').classList.add('post-active');
      scrollToActiveTocLink();
    }
  }
}
handleActiveTOCLink();

I can use a sketch as a map, too

I sometimes want to use sketchnotes as overviews, especially if I've added hyperlinks to them. I used to make the images show up on the right side, but now I want them to show up in the left-side navigation instead. Also, I wanted any links to headings to automatically get recoloured as I scroll to that heading.

2025-04-02-scrolling-svg.gif
Figure 2: Animated GIF showing how the SVG highlights change as you scroll down

I added a special case to the handleActiveTOCLink function to handle anchor hyperlinks (just #anchor) in the SVG. It probably makes sense to make those absolute URLs, which means slightly changing my workflows for hyperlinking SVGs and writing about sketches.

So on both the category page (ex: the Hyperlinking SVGs entry in category - drawing, which might have moved off the first page of results if you're reading this far in the future) and the single-post page (ex: Hyperlinking SVGs), there's a full-sized version of the image in the main blog post, and then a small copy of it in the margin on the left. The sidebar copy is probably too small to read, but it might be enough to get a sense of spatial relationships, and the links also have title attributes that are displayed as tooltips when you hover.

2025-04-03_14-28-48.png
Figure 3: Screenshot of small image in sidebar on the single post page

I use Javascript to duplicate the image and make a small, sticky version because I haven't quite figured out how to properly make it sticky when off-screen with just CSS. Even my JS feels a little tangled. Maybe this would be a good excuse to learn about web components; someone's probably figured out something polished.

I'm curious about using more drawings to anchor my thinking and structure my blog posts.

Progressive enhancement

Some people read my blog using EWW (the Emacs Web Wowser, of course), so I want my blog to be reasonable even without CSS and JS.

A number of people read my blog without Javascript enabled. I installed the Firefox extension Script Switch so that I can test my blog with and without Javascript whenever I remember.

I sometimes look up my blog posts on my phone and there's no space for any of this fanciness there, so it'll only kick in on large screens. My CSS file is littered with various breakpoints I've cargo-culted over the years and I should simplify it at some point. At the moment, if it looks fine on my Lenovo P52, I'm happy.

Other ideas and next steps

Theoretically, the right margin is now available for sidenotes, so I might be able to look at ox-tufte and Eleventufte and get something going. Then I'll have a way to add small notes that are shorter than a paragraph. Longer tangents can go in a details/summary element instead, although I have it on good authority that one can write at length in footnotes. I love the footnotes in the Bartimaeus series, and apparently there are quite a few books where the footnotes are part of the storytelling.)

It might be nice to let tables extend into the right sidebar when I know I won't have a doodle nearby. Incidentally, Sidenotes In Web Design · Gwern.net uses breadcrumbs in the left sidebar instead of a table of contents, so there's more space for tables and sidenotes.

I thought about using CSS breakpoints so that on a medium-sized screen, we can have the left sidebar even if there's no space for something on the right. I haven't gotten around to experimenting with it yet, though. Besides, I don't know yet if I want to prioritize the stuff I want in the right sidebar (side notes, doodles) over fairly-static navigation.

As I mentioned, it might be handy to tweak my SVG linking workflow to use absolute URLs.

Sometimes I look up my notes within Emacs, but surprisingly often, I look them up on the Web. Navigation isn't just cosmetic. I want to get better at using my blog as a tool for thought, so tinkering with layout isn't just window dressing. It's (very slowly) experimenting with scaffolding for my brain. Little things can help!

Using Emacs Lisp to batch-demote HTML headings for my static site

| blogging, 11ty, emacs

Assumed audience: People who have lots of HTML files used as input for a static site generator, might need to do a batch operation on them, and are open to doing that with Emacs Lisp. Which might just be me, but who knows? =)

HTML defines a hierarchy of headings going from <h1> to <h6>, which comes in especially handy when people are navigating with a screenreader or converting web pages to Org Mode. I think search engines might use them to get a sense of the page's structure, too. On my blog, the hierarchy usually goes like this:

  • <h1>: site title,
  • <h2>: blog post titles, since I put multiple blog posts on the main page and category pages (ex: blogging)
  • <h3>: blog post's subheadings, if any
  • <h4>: I rarely need subsubheadings in my main blog posts, but they're there just in case

While fiddling with my blog's CSS so that I could try this fluid type scale, I realized that the subheadings in my exported blog entries started at <h2> instead of <h3>. This meant that the outline was this:

  • Site title
    • Blog post 1
    • Subheading 1
    • Subheading 2
    • Blog post 2
    • Subheading 1
    • Subheading 2
    • Blog post 3

I wanted the outline to be this:

  • Site title
    • Blog post 1
      • Subheading 1
      • Subheading 2
    • Blog post 2
      • Subheading 1
      • Subheading 2
    • Blog post 3

This was because I hadn't changed org-html-toplevel-hlevel during my 11ty export process. To solve this for new posts, I added a new option org-11ty-toplevel-hlevel that defaults to 3 in ox-11ty.el, re-exported one of my long blog posts to test it, and confirmed that my headings now started at <h3>.

I still had all my old HTML files with the wrong levels of headings. I wrote some Emacs Lisp to shift the headings downwards (h5 to h6, h4 to h5, h3 to h4, h2 to h3) in a file if it had an <h2> in it. Regular expressions are usually not a good idea when it comes to HTML because there might be exceptions, but I figured it was a pretty small and low-risk change, so I decided not to use the full XML/DOM parsing functions. I saved all the blog posts under version control just in case I messed things up. Here's my function:

(defun my-html-shift-headings (filename)
  "Shift heading tags in FILENAME."
  (interactive "FFile: ")
  (let ((case-fold-search t)) ; make the search case-insensitive
    (with-temp-buffer
      (insert-file-contents filename)
      (goto-char (point-min))
      ;; Only modify the files where we have an h2
      (when (or (search-forward "<h2" nil t)
                (search-forward "</h2>" nil t))
        (goto-char (point-min))
        ;; Handle both opening and closing tags
        (while (re-search-forward "<\\(/\\)?h\\([2-5]\\)\\>" nil t)
          (let* ((closing-tag (match-string 1))
                 (heading-level (string-to-number (match-string 2)))
                 (new-level (1+ heading-level)))
            (replace-match (concat "<" closing-tag "h" (number-to-string new-level)))))
        (write-file filename)
        filename))))

Running it on all the source HTML files in specific subdirectories was easy with directory-files-recursively.

(dolist (dir '("~/proj/static-blog/blog"
               "~/proj/static-blog/content"))
  (mapc 'my-html-shift-headings
        (directory-files-recursively
         dir
         "\\.html\\'")))

Then I could just rebuild my blog and get all the right heading levels. Spot-checks with Inspect Element show that the headings now have the right tags, and org-web-tools-read-url-as-org now picks up the right hierarchy for the page.

Correcting the input files was easier and more efficient than modifying my 11ty template engine to shift the heading levels whenever I build my site (probably by defining a preprocessor). I could've written a NodeJS script to do that kind of file manipulation, but writing it in Emacs Lisp matched how I might think of doing it interactively. Using Emacs Lisp was also easy to test on one or two files, check the list of files matched by directory-files-recursively, and then run it on everything.

Going forward, the new org-11ty-toplevel-hlevel variable should properly modify the behaviour of Org's HTML export to get the headings at the right level. We'll see!

Monthly review: March 2025: going on field trips; shifting from fretting to learning

Posted: - Modified: | monthly, review

[2025-04-02 Wed]: Added emoji summary.

Summary

March 2025: 🔥🎵🥧🍎✏️⛸️📰😴⏲️🍕🖨️🏝️🎮📚🎵🎮🐒🦷👥🧁🏺💭📺🐟🚲🧁🥾🌧️📝🛒

Text from sketch

March 2025:

decrease fretting, increase experiments, field trips, self-efficacy

  1. 🔥 campfire
  2. 🎵 Minecraft & singing
  3. 🥧 peach pie
  4. 🍎 apples & marshmallows
  5. ✏️ doodling
  6. ⛸️ skating in the wind
  7. 📰 RSS, thoughts on reading
  8. 😴 tired kids
  9. ⏲️ cubing comp
  10. 🍕 pizza playdate
  11. 🖨️ creeper 3D print
  12. 🏝️ quiet day, watched Moana 2
  13. 🎮 arcade
  14. 📚 bookstore, meds
  15. 🎵 music theory
  16. 🎮 LEGO Incredibles
  17. 🐒 monkey bars
  18. 🦷 tooth: UR2
  19. 👥 playing with the group
  20. 🧁 Sand cakes with A-
  21. 🏺 pottery wheel
  22. 💭 feelings
  23. 📺 TV
  24. 🐟 Ripley's Aquarium
  25. 🚲 bike playdate
  26. ☕ hot chocolate
  27. 🧁 chocolate cupcake
  28. 🥾 trails
  29. 🌧️ freezing rain
  30. 📝 homework lap
  31. 🛒 proud of buying snacks & cereal

That was actually a pretty full month. I liked the little field trips we went on, and the way A+'s been so proud of her growing self-efficacy and her singing, and how we've been getting together with friends, and how we've been enjoying more produce, and how we've been learning… Nice.

2025-03-31-05

In February, I wrote I wanted to:

  • Practise being calmer and more easygoing; get better at thinking in terms of experiments
    • Progress!
  • Take A+ on more informal field trips: Royal Ontario Museum, pottery class
    • Didn't make it to the ROM, made it to pottery and other places
  • Start digging into the ideas and tasks I've been postponing
    • Barely any progress on this; I keep coming up with new ideas!
  • Finish the Simply Piano course and start working on sheet music for the songs A+ wants to sing
    • I managed to finish the beginner course and now I'm mid-way through the intermediate ones
  • Practise singing scales while waiting for A+ to catch up with Simply Sing
    • She caught up and went beyond me, yay! I'm still definitely off-key, but we can sing enough to have fun
  • Automate the BigBlueButton setup a bit more so that I don't forget about meetups
    • … forgot to check on my automation, but was able to scramble things together in time for a meetup

The weather's starting to warm up. Skating season is pretty much over, but now we're moving into biking season and picnic season, so that's all good. We had a couple of outdoor playdates and we hosted one of her friends for backyard pizza-making. A+'s been biking more, too. Hooray!

I continue to work on fretting less about A+'s schoolwork. Creativity and playfulness tend to work better for her anyway. For example, she tends to respond well if I invite her to do the homework on my lap, or if I challenge her to dictate her math answers in Chinese, or if I ask her to answer while she's upside down. Scheduling my fretting for one day a week seems to help me not worry too much the rest of the time. On Saturdays, I make a checklist of work that still remains, but I'm not terribly attached to whether it all gets done that day. I find it easier to back off when I remind myself of the long-term perspectives:

  • I want her to figure out how to do things even without me pushing.
  • Failure is data. Early failure is useful, too. It's good to try things out while the stakes are low.
  • It might not be a problem. Even if it's a problem, it might not be her problem.
  • School is a small part of the picture, and grades just provide feedback on whether A+ has demonstrated the skills they're looking for in a way that they can evaluate. Some things are easier if you do well in school, but there are many other paths.
  • This is her experiment, not mine.
  • Also, learning how to let her try things out for herself will help me even more when she becomes a teenager, so it's good for me to learn while the stakes are low too

An easy way to distract myself from fretting by focusing on the things I want to learn and do. I've added piano practice to my daily routine, which is an interesting way to check on how distractible my mind is. There's also writing, coding, tidying up around the house… Plenty of things to keep me busy.

I've been going for more walks, too. Finally started tromping around the paths in the park. I usually clip my lapel mic on for those walks, since they're a good opportunity to braindump. I'm definitely not as coherent as Prot, but maybe with practice, I'll get the hang of exploring my thoughts in a linear way. One of the nice things about thinking out loud this way is realizing quite quickly where the limits of my thoughts are, where I trail off into vagueness or tangled words.

I like to draw as a way to help me untangle my thoughts. I enjoyed drawing more this month, including some fun experiments with drawing feelings. I ordered a screen protector and a pencil grip from Paperlike. With that, I think I'm fairly comfortable drawing on the iPad now. My SuperNote A5X has been a bit neglected, but there's been a system update that added stickers, so that might be fun to check out. Drawing is one of the types of homework that A+ regularly procrastinates, but she responds better if I draw along with her. It's great. I get a weekly source of drawing prompts thanks to Grade 3 structured literary analysis homework. I haven't been following drawing tutorials as much these days. Maybe that's something that might be fun to pick up again.

It's getting easier and easier to talk myself down from fretting as I watch A+ enjoy the growth in her self-efficacy. She can do so many more things for herself now. She figured out how to swing across the rotating monkey bars that she couldn't reach before, and connected the idea of momentum to homework. She was proud of getting to level 7 in the Simply Sing app, and of her 6+-week streak. She experimented with different story ideas. She hiked through freezing rain with her nature club. She even proudly chose and bought herself some groceries.

A+ asked for more field trips, so I tried to plan at least one interesting activity a week, sometimes pulling her out of virtual school after afternoon recess. It worked out really well, I think. We were able to enjoy the sunshine, spend time exploring the world around us. QUick notes:

  • cubing comp (A+ did 2x2, 3x3, and Pyraminx): A+'s been going to an online cubing club, so she's been practising on and off, but her current hyperfocus is singing instead of cubing. Still, she was happy with how she did, and she enjoyed volunteering as a judge. This was a bit of a last-minute registration for us, so I've added a TODO to check for upcoming competitions at the start of the year.
  • a public library with 3D printers: A+ wanted to try printing a model that she made at the library's Tinkercad workshop. She also wanted to print a cube stand. Both worked out pretty well. It was a nice bike trip to the Fort York library. A+ hasn't come up with more print ideas yet and neither have I, but it's nice to know that the resources are available.
  • a pottery wheel lesson: she's interested in more pottery wheel practice and also a painting workshop. She's not interested in hand-building yet and she still needs my help with the wheel. Could be fun to explore together.
  • Ripley's Aquarium: she enjoyed petting the stingrays and looking at the sharks. I forgot to take advantage of the Presto discount, whoops! Anyway, I'll see when A+ wants to go again. If I think we'll be there frequently, then we can get a membership.
  • The homeschoolers' playgroup: It was nice to reconnect with friends at the park now that they're coming out of hibernation.

A+'s a little behind on schoolwork, but I think that's more of a matter of motivation rather than knowledge, and we'll be able to catch up. I'm looking forward to exploring more things next month and seeing what we can get away with.

Time

Category Previous month % This month % Diff % h/wk Diff h/wk
Discretionary - Productive 11.2 17.4 6.2 32.3 10.5
Personal 7.4 10.9 3.5 20.3 5.9
Discretionary - Play 0.2 0.9 0.7 1.7 1.2
Unpaid work 3.2 3.8 0.6 7.0 1.0
Discretionary - Social 0.0 0.0 0.0 0.0 0.0
Discretionary - Family 0.1 0.1 -0.0 0.1 -0.1
Sleep 33.9 33.3 -0.7 61.8 -1.1
Business 2.4 1.0 -1.3 1.9 -2.2
A+ 41.9 32.6 -9.3 60.5 -15.6

Thanks to my resolution to fret less, I'm spending less time on childcare (A+) and more time exploring my own interests (Discretionary - Productive). I've even been doing a little less consulting. It feels good. I'm still around in case she wants help, but now I get to explore things I want to do.

Let's break that discretionary/consulting time down:

Category Previous month % This month % Diff % h/wk Diff h/wk
Discretionary - Productive - Music 1.6 6.0 4.4 11.2 7.4
Discretionary - Productive - Coding 0.2 1.7 1.6 3.2 2.6
Discretionary - Productive - Emacs 1.6 2.9 1.3 5.4 2.2
Discretionary - Productive - Nonfiction 0.0 0.7 0.7 1.3 1.2
Discretionary - Productive - Drawing 2.9 2.2 -0.7 4.2 -1.2
Discretionary - Productive - Writing 4.5 3.8 -0.7 7.0 -1.2
Business 2.4 1.0 -1.3 1.9 -2.2

Ah, yes, mostly music. We have a Yamaha YDP-113 piano which is finally getting played, yay me (and yay W-'s decision to get it about two decades ago). I want to get the hang of at least Simply Sing's intermediate lessons so I can finally play those Disney songs at a consistent tempo, which means A+ will be able to sing instead of having to stop and restart as I stumble on notes. Plus music has a way of gently pointing out when my attention wanders, so there's that. This feels like a reasonably good use of my time.

Ideas for next month

  • Get better at enjoying life with A+ and W-
    • Keep replacing fretting with snuggles and connection
    • Look for more field trip possibilities
    • Explore more recipes, especially as more local produce becomes available
  • Playdates:
    • Popsicles, biking, farmers markets… fun fun fun!
  • Gardening:
    • Start some seedlings indoors (bitter melon, cherry tomatoes, marigolds, basil; maybe cucumber?)
    • Convert the rest of the grass into garden and amend the soil
    • Direct-sow lettuce, poppies
  • Sewing:
    • Might be good to start thinking about sewing more skirts and dresses for A+ and me, and probably new swimwear too.
  • Habits:
    • Continue with piano, walking/biking, writing, drawing
    • Ease into some kind of strength thing? Grip strength might be a good place to start. Maybe I can learn some hand-strengthening exercises I can do next time A+'s doing homework on my lap.
View org source for this post