I'm a full-stack web developer, and this is my blog. Please connect with me on LinkedIn or visit my Github for more! Also, you may be interested in learning more about me.

Projects

  • Six Things I Bet You Didn't Know You Could Do With Chrome's Devtools, Part 1

    A screenshot of the Chrome devtools panel showing the Elements tab inspecting google.com

    I just got back from TechBash conference in Pennsylvania. It was a great couple of days of meeting new people, reconnecting with old friends, and of course learning a ton.

    Many of the sessions I went to were fantastic, but my favorite session by far was by Mike Rapa about the power of the browser’s devtools. His presentation was (mostly) browser-agnostic, with the exception of a few things, because (and I can attest to this) Firefox’s devtools have markedly improved over the last few years. This post, however, is specific to Chrome, for reasons you will see soon.

    So, with thanks to Mike, and a few other sources, here are six things I bet you didn’t know you could do with Chrome’s devtools.

    This is part 1 of this topic, covering items 1-3. I wrote way too much for a single post!

    In this post we’ll cover:

    • Time functions with console.time() and console.timeEnd()
    • Watch any DOM element for changes
    • Monitor any function in the browser’s context

    In the second post, coming soon, we’ll cover:

    • Edit any website WSYWIG style
    • Record and replay any user actions
    • Throttle only specific network requests

    So let’s get going!

    1. Time functions with console.time and console.timeEnd

      I have been vaguely aware of the idea that console.log() is just one method available in a vast cornucopia of logging methods, and I have dabbled with console.warn() and console.error(). I cannot say that I have ever used console.table(), although I knew it existed.

      Had I ever, however, even heard of console.time() and console.timeEnd()? No, and I bet most people haven’t. I recently needed to track down a bug where a window.setTimeout function seemed to be clearing itself earlier than expected, and so I wanted to log when a timer was set, for how long it was supposed to be set for, and when that timer cleared. Searching around yielded a lot of people suggesting complex options, including overloading console, writing wrapper functions, and so on, implying that these built-in functions are not part of most folks’ knowledge.

      On the other hand, console.time() and console.timeEnd() seem like just what the doctor ordered.

      Example from MDN:

       console.time("answer time");
       alert("Click to continue");
       console.timeLog("answer time");
       alert("Do a bunch of other stuff…");
       console.timeEnd("answer time");
      

      More interesting example:

           const [paused,setPaused] = useState(false);
           const [timeoutId,setTimeoutId] = useState();
      
           const handleClick = ()=>{
               if (paused){
                   console.log("unpausing")
                   clearTimeout(timeoutId);
                   console.timeEnd(timeoutId.toString());
                   setPaused(false);
               } else {
                   console.log("pausing for 5 s, or when the user clicks the button next, whichever comes first")
                   setPaused(true);
                   const timeout = setTimeout(()=>setPaused(false),5000);
                   console.time(timeout.toString());
                   setTimeoutId(timeout)
               }
           }
      
           return (<>
           <button onClick={handleClick}>{paused?"Unpause":"Pause"}</button>
           </>);
      

      Source: Mike Rapa’s talk, which I couldn’t find a recording of, but the github repo is here and is fantastic.

      Support: This is part of JS, so any browser.

    2. Watch a DOM element for changes and pause app execution when it does.

      The DOM Breakpoints option lets you select an element from the DOM and, when something on the page would cause it to change, pause execution of the JS that is causing the change, entering Chrome’s debugger mode. I have known about placing debugger; on a line in my JS/React to trigger debug mode, and of course there’s also the pause button on the Debugger tab, but a DOM breakpoint was also new to me.

      Visit this SO page and run the code snippet in the answer. If you inspect the element as it’s running, you can see the DOM highlight as it updates:

      If you were to right click on it, choose “Break On”, and one of the options, you will now hit a breakpoint when that DOM element is modified, allowing you to see what Javascript is affecting this element. Demonstration of adding a DOM breakpoint through the context menu

      Source: Mike Rapa’s talk

      Support: Most major browsers, although Firefox behaves slightly differently than Chrome

    3. Attach a listener to any function on the page

      Say you’re debugging some interaction in some third-party script that you don’t control. (Something that used to come up a lot on my old team.) You’d like to know when the third-party script is doing something, but you can’t exactly go into that script’s code and add console.log() (or one of the fancy console methods that we just learned about).

      Chrome lets you attach a listener to any function in the browser’s context, with monitor.

      Contrived example:

       function sum(x, y) {
           return x + y;
       }
       monitor(sum);
       sum(1,2) //results in console.log message: "Function sum called with arguments: 1,2"
      

      Source: Google documentation

      Support: Chrome only

    That’s it for the first half of this post! Come back later to see the next three tips.

  • The Surprising Power of Jekyll's site.data

    A stereotypical, "hacker-like" depiction of a database, with glowing blue lines signifying connections between tables.

    I’ve been speaking a lot this past year. This is new and exciting to me, and I want to track all my accomplishments, because each one of them feels new, exciting, and honestly a little scary. So keeping track of everything is a great way for me to mark these accomplishments (I may also have had a donut after my last talk at Momentum).

    But do I want to manually update this page every time something changes? Heck naw. This is structured data, and Jekyll should treat it as such.

    Enter site.data

    Enter site.data. Any structured file (JSON, YAML, CSV or TSV) you place in your Jekyll _data folder will become accessible as site.data.filename 1. You can then access it like any other variable. Jekyll is smart enough to interpret your file as a list which you can sort, filter, etc.

    What this means is I can create a CSV like this:

    event_date,venue,talk,link
    2025-10-16,Momentum Dev Con,Get Unblocked Faster,,
    ....
    

    and then access it via:

    
    {% assign all_appearances = site.data.speaking_appearances | sort:'event_date' %}
    
    <h2>Upcoming</h2>
    {% for row in all_appearances %}
    {% assign date_to_check = row.event_date | date: '%s' %}
    {% if date_to_check > current_date %}
    <p> {{row.event_date | date: '%B %d, %Y'}}:
    {%if row.link%}
    <a href="{{row.link}}">
    {%endif%}
    {{row.talk}} -
    {%if row.link%}
    </a>
    {%endif%}
    {{row.venue}}
    </p>
    {%endif%}
    {% endfor %}
    
    

    This looks like a lot if you’re not used to Liquid syntax, but it’s basically a fancy templating language. On the first line, we assign a variable to hold the “list of stuff in site.data.appearances, sorted by event_date” (which is one of the headers in the CSV). Then we loop through each item in the list and compare its date, which has been converted to a UNIX timestamp, to the current date (which I assigned earlier in the template). If the item has a date in the future, we render it to the page, optionally with a link to the source material.

    This is already a huge time-saver, and means that all my dates, links, etc., will be rendered consistently, and I can change their styling/look without having to manually copy and paste.

    But let’s not stop there. I don’t want to manually update a CSV that lives on my home computer (or in a github repo), that sounds boring. So inspired by my husband Chris Combs who has set up something similar, let’s automate this!

    Enter a database that is not a database

    Google Sheets is not a database, although I wouldn’t be the first person to use it as such. And for a simple way to store (a small amount of) structured data2, it’s pretty darn good.

    So I copy my CSV from above into a google sheet and publish it to the web as a CSV. This does make the CSV publicly accessible for anyone who knows the URL, but I’m not storing any PII in here, so it’s fine. Then, as an extra line in my custom deploy pipeline as well as my custom preview script, I add:

    wget https://docs.google.com/spreadsheets/link/to/spreadsheet?output=csv --output-document=${SITE_ROOT_DIR}/_data/speaking_appearances.csv || echo "Something went wrong"
    

    This means that every time I deploy, or launch a local version of the site with bundle exec jekyll serve, I get the most up-to-date version of the Google sheet, which I can update from anywhere.

    Jekyll is still a static site generator, so I still have to deploy to see the most recent changes, but that works for me, for this thing that is only semi-regularly updated anyway. If this is not for you, you probably already know that and will not use Jekyll.

    I hope this was useful! I definitely think there’s a lot more that can be done with Jekyll’s datafiles and I can’t wait to come up with more insane ideas.

    Further reading

    • Jekyll’s official site has some very cool examples of things you can do with a datafile.

    Footnotes

    1. for this reason you should avoid having two files with the same base name but different extensions. Which seems like a silly limitation but oh well. 

    2. if I give ten million talks this solution may not scale. But I think a number of things about this hypothetical would become unsustainable first. 

  • Accessibility the Easy Way With Deque's Linter

    I have been interested in, though never an expert in, web accessibility, for years. I believe common-sense affordances like alt text and keyboard navigability in a website are shining examples of the curb-cut effect, so while I am not (currently) disabled1, I do at least try to make my websites accessible.

    But I didn’t know until attending Abbey Perini’s talk at CodeWord last week that I could automate at least some of this work with an accessibility linter.

    Wait, a what?

    Yeah, my reaction too. But just like Prettier or eslint can enforce code style rules, an accessibility linter can enforce, or at least warn you about, accessibility violations. I immediately downloaded deque’s axe Accessibility Linter plugin for VSCode and it’s already caught a few things on this here blog. (I may have forgotten alt text in a few places…)

    Accessibility is much more than automated tools. A site could pass the linter and pass all kinds of other tests and still be difficult to use. But this is one new tool in my toolbox that I didn’t know about before, and I’m glad to have it.

    Watch Abbey’s talk for yourself on Youtube.

    1. The stats vary, but some say that 1 in 3 Americans will experience a temporary disability of 90 days or more before reaching the age of 65. That’s a lot. 

  • Who's Touching My Files? Watch Out With Watchman

    A photo showing an old (possibly illegitimate as I got it off a .ru site) Game and Watch console. Is Mr. Game and Watch a watch-man? I don’t see why not.

    I have a weird setup at work with a lot of interlocking config files. (We sort of halfway support git’s sparse-checkout feature but with an internal tool that’s supposed to keep everything in sync outside of git. Don’t ask. It’s better than I’m making it sound.)

    Anyway because of this funky setup, I was running into an issue where a file was being modified, incorrectly, when I ran some command. I wasn’t sure which command was causing the ghost line to reappear, because it kept happening behind my back. It was driving me crazy, and I tried all sorts of solutions to ‘catch’ the file appearing.

    What finally worked was watchman, a little file monitor tool that Facebook developed for web development. What it’s supposed to do is monitor a directory for changes, and then run some sort of command on changed files. It runs in the background and automatically remembers your watches even if you reboot your machine, which cannot be said for inotifywait or fswatch or any of the other things I tried. It also works on Windows, Mac and Linux, because under the hood it hooks into one of the above utils depending on your OS. In other words, it Just Works(tm).

    The canonical watchman example is running a CSS minifier any time CSS within a certain directory is modified.

    watchman watch path-to-dir

    watchman -- trigger path-to-dir name-of-trigger '*.css' -- minify-css

    In my case, I don’t need to run any sort of processor on changed files, I just need to know who or what is changing them. Easy:

    watchman watch path-to-dir

    watchman -- trigger path-to-dir mytrigger 'the-file.txt' -- say "I changed"

    This was so silly (and for a while my computer was making a horrendous number of annoying noises) but it worked great and helped me track down which unrelated command was changing my config file.

    Learn more about watchman from the official docs and give it a shot.

    P.S. the official docs have about 3 suggestions for where to find watchman’s logs– I found mine in none of those suggestions, but I was able to run watchman get-log to find the (quite mysterious) location.

> > >Blog archive