aaron's mask
- - - - - -
-
-
- Commission piece; based on an existing pattern. The red detailing and teeth are my own design.
- -diff --git a/_site/1/index.html b/_site/1/index.html deleted file mode 100644 index ce6b37bf..00000000 --- a/_site/1/index.html +++ /dev/null @@ -1,1220 +0,0 @@ - - -
- - - - - -(or browse by tags)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (or browse by tags)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (or browse by tags)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (or browse by tags)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (or browse by tags)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (or browse by tags)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (or browse by tags)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (or browse by tags)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (or browse by tags)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Head home.
- -(or browse by tags)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (or browse by tags)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (or browse by tags)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (or browse by tags)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (or browse by tags)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Commission piece; based on an existing pattern. The red detailing and teeth are my own design.
- -My name is Lee Cattarin. I use he or ze pronouns.
- -I'm a programmer (are you looking for a resume?), artist&crafter - knitting, spinning yarn, stamp carving/printmaking, cardmaking & papercrafts, bookbinding, leatherworking - bread baker, guitar player, and probably a lot of other things.
- -
-
-As of November 2023, my wife Brooke Osment and I have a art store: Riverside Refuge Studio. You can find various store links, or other ways to connect, on my contact page. We're based out of Vashon, WA, USA, and ship internationally.
- -We have a dog...
- -
-
-...and six ducks.
- -
-
-
-
-
- A green journal with a blue leather spine. The coloring pages are from the book Color Acadia with art by Katie Dube and Keri Kimura.
-
-
-
- Yarn: The Fibre Co. Acadia
- -
-
-
- Recently I've been working on a single-page digital rendition of a zine complete with many hand-drawn images. The author wanted to be able to bring images up to a full-screen view, to either zoom in or to put the whole enlarged image on one screen with no scrolling. It was a real struggle to find resources on how to do this in an accessible manner, so I'm writing up what I did.
---Fair warning: This solution is likely imperfect.
-
dialog elementdo you know how many tutorials want you to roll your own modals? It's a not-insignificant amount. W3Schools, top of the search results in many cases, recommends it in two places - image modals and responsive image modals. Several search results for "image modal" pop up div solutions - div modal 1, div modal 2, div modal 3.
-If you don't know of the existence of <dialog>, a search for image modals will not get you there quickly.
If you search for accessible image modals, you'll still hear about <div>s. Hell, W3C's ARIA Authoring Practices Guide uses a <div>. You kinda have to go digging to read about the dialog element from Scott O'Hara or find AccessibleWeb.dev's piece on modals. Or you can go straight for Adrian Roselli and find examples that use the native <dialog> element. Thanks Adrian!
Did I roll my own modal at first? Regretfully, yes. I'd used the <dialog> element before... several years ago... in a project I don't have access to anymore... Needless to say, I had forgotten about its existence.
Anyway, I got there. Eventually. So let's talk about modals and <dialog>.
<dialog> give us?so, what do we get from the <dialog> element that we don't get from a <div> modal?
::backdropEsc key to close the modal.showModal() and .close()closedby is set to any, clicking outside the modal (anywhere on the backdrop) will also close the modalautofocus attribute for in-modal elements to set which element should receive focus on opening the modalopen attribute which is set on the <dialog> when open and removed when closed (when you use the functions mentioned previously)and more. This is just the pieces in use for me. There's also things like setting forms so that submission closes the dialog (point 1 in that list).
-.close() call (or rely on users hitting escape or clicking the backdrop)MDN warns:
---Do not add the
-tabindexproperty to the<dialog>element as it is not interactive and does not receive focus.
— <dialog>: additional notes (point 3 in that list).
despite this, I found that in Firefox (but not Edge), the <dialog> itself was focusable. I have no idea why, but I tested this on a totally unstyled and unmodified page and still found it to be true. As a focusable element, it made no sense. It had no interactivity and could not be activated.
I'm still torn: do I add tabindex="-1"? MDN specifically says not to, but I'm pretty sure they're warning against making it focusable. Why warn against making it nonfocusable when it's not supposed to be focusable in the first place, after all?
At current, I'm ambivalent, but I've added tabindex="-1" to handle Firefox's poor behavior. Making the dialog focusable is unhelpful and confusing.
<dialog>here's what else I wrote...
-besides the <dialog>, I gave my <img> elements tabindex="0" to make them focusable.
in modal.js, I created an openDialog() function that takes in the clicked image. It:
<img> element and copies over the src and alt attributes - importantly, it doesn't copy the full <img> because we don't want to copy that tabindex attribute<img> in the modal with our new copy with replaceChild() (or if there's no current one, it just appends)dialog.showModal()I gave my close button two event listeners, one that listens for click events and one that listens for a keydown of the space or enter keys. In the case of the keydown, it calls event.preventDefault() to stop the space key from scrolling the underlying page.
I also looped through all images and attached my openDialog() function to any image with a tabindex attribute (I had some images that weren't intended to be fullscreened, so they lacked tabindex). Again, I gave them listeners on both click and keydown.
here's the most relevant parts of the CSS:
-dialog::backdrop was given a relatively calculated color - rgba(from var(--color-bg) r g b / .8) - as well as a blurbody:has(dialog[open]) has overflow: hidden setdialog img uses a max-height as well as object-fit: containreach out!
- -
-
-
- alrighty, this one's a real doozy. Strap in.
-have you ever tried to revert to a previous version of a document in MS Office or Google Docs and found that your revision history is cluttered with small changes that by all rights should be grouped into one set of edits, but they aren't, and it's tedious to pick through all the versions?
-or, more uniquely and/or uncommonly:
-or, websites. Maybe you're building something in Squarespace and find out that in current versions of Squarespace, it doesn't support version history..
-I started writing this to help a friend. She's getting started with a website, and we're using the static site generator 11ty as she wants to have a lot of easy-to-write posts. She needs a single-user workflow that allows her to publish her website without hosting it herself, and that's the use case where this was born.
-this walkthrough is best suited for people who want to use git in single-person projects, or perhaps with one or two other close collaborators. There's quite a few topics it doesn't cover that are vitally important in large collaborative projects, such as branching and merging.
this walkthrough also focuses on the "happy path," without much discussion of troubleshooting. I may write more on the topic in the future, but we're already over 4,500 words, so we're calling it a day.
-finally, I wrote this walkthrough primarily with knowledge from using WSL [more on this later] on Windows and with Zed as my text editor. While I've tried to cover my bases with other OSes and options, there's a solid chance I'm missing things!
-that all said, let's get (git?) into it!
-git?git is a version control system. We can use it to track changes we make to a set of files.
--tip: it's important to understand that despite the examples of MS Office and Google Docs above,
-gitisn't useful with word documents.gitshines with plain text files - .txt, .md, or basically any type of code.
git?many, many tools interact with git:
git operations, like VSCode, Zed, or Sublime Textgit GUIsgit users use the git command line interface (CLI), which is fully text-basedtoday we're going to talk about the git CLI... technically. But don't let that scare you - we'll talk about concepts and actions that can be applied to other git interfaces as well.
a CLI a way to interact with your computer and with software in text-only form. Rather than using the mouse and clicking on things, you type in commands and see output.
-git CLI?if you want to use the git CLI, you'll need a terminal. You've got a couple options here:
git generally ships with these systems, so there's no installation required. Search for an application called 'Terminal' or similar
-git for Windows packages a Linux-like terminal with a git GUI. This may be slightly friendlier for people who aren't at all familiar with Linux. If choosing git for Windows, see my installation instructions belowgit for Windowson the "Releases" page, scroll down to "Assets" and pick the .exe file.
during installation, you'll be asked to choose some things by the installer. Here's my recommendations:
-nano, an in-terminal editoryou can open git for Windows via the start menu by searching for 'git bash.' If you chose to install Windows Terminal, one of the dropdown options will be for a new tab will be 'Git Bash.'
while installing WSL is a single command, here's a couple notes about getting started once inside WSL:
-Ctrl+C and Ctrl+V won't work like they do on Windows. If you want to copy/paste, right-click (there won't be a context menu, it'll just happen)you'll also need to be careful of a few things regarding text editors to make them work with WSL:
-you must start your editor of choice within WSL. Don't use the Windows start menu! Instead, you'll type code . or zed . (note the .) while in WSL.
you can open WSL via the start menu by searching for 'WSL.' If you chose to install Windows Terminal, one of the dropdown options for a new tab will be your WSL distribution, usually 'Ubuntu.'
-here's three vital terminal commands:
-pwd prints the working (current) directory. A lot of terminals will just show you what your current directory on every line, but if they don't, try pwd.--tip: directory is just another word for folder
-
cd lets us change directories. If we type only cd, we'll be brought back to the home directory; if we provide a directory path, we'll be taken to the provided directoryls lists files in the current directory (including other directories)we'll want to edit files, right? How do we open our editor from the terminal?
-there's usually a terminal command for the editor. For VSCode, it's code; for Zed, it's zed. If we want to open the current directory in our editor of choice (and we do!), we'll write <editor command> . (note the .), where . means "the current directory."
git versionlet's check that you have git installed with git version. You might see something like git version 2.34.1 printed out in response. If you don't get a version number, but instead get an error saying you don't have git, install git.
before we really start, we're going to set a few basics to make it easier for ourselves.
-# skip this one if you installed git for windows
-# this means that if git wants us to edit something,
-# it'll open in the built-in terminal editor 'nano'
-# the default is vim, which can be pretty unfriendly to newcomers
-# nano, on the other hand, will tell you how to do basic
-# operations at the bottom of the editor
-git config --global core.editor nano
-
-# this uses the autocorrect
-# the value specifies how many *tenths* of a second
-# so 10 => 1 second
-git config --global help.autocorrect 10
-
-# skip this one if you installed git for windows
-# the default branch name is "master" due to older computer terminology
-# older language used to explain some computing relationships as master/slave
-# some people consider this outdated and harmful, so "main" is a more common these days
-# also, I'll be using main, so this will help make your output look like mine
-git config --global init.defaultbranch main
-
-# this sets our information
-# if we don't set this, git will prompt us to set it later
-git config --global user.name <your-name>
-git config --global user.email <your-email>
-git going(no, that's not a real git command.)
there's two main ways to start:
---tip:
-gitand associated tooling refer to projects as repositories. I'll be sticking with the word project here as I find it a bit friendlier, but you'll probably run across the word repository in the wider world ofgit
git initgit init <project> will create a new directory named project ready to be used with git. We can then use cd <project> to enter the directory.
--tip: don't use spaces in your project name!
-
git clonegit clone <project URL> will pull in an existing project. We're not going to talk about this right now; instead, we're going forward assuming with git init.
git statusbefore we do anything, let's see what git will tell us about our project. Type git status and we might see the following:
On branch main
-
-No commits yet
-
-nothing to commit (create/copy files and use "git add" to track)
-let's dissect this.
-git has a concept of branches, which are different paths our file history has taken. While branches are incredibly powerful, we're going to stay away from branches during this walkthrough and focus on working on a single branch - in this case, main.
"no commits" means that the project has no history whatsoever. "Nothing to commit" means we've made no changes. But what is a commit?
-a commit is one set of changes made to our work. We get to choose which changes are part of any given commit, and we write a message describing the commit so that future-us knows what we did if for some reason we need to undo something.
-git login an established project, we can use git log to look at our commit history. By default, one commit will output like this:
commit e2fd6c4772e61f9c074638a933eb92fc1ea885ef
-Author: Lee Cattarin <lee.cattarin@gmail.com>
-Date: Sun Dec 28 18:47:00 2025 -0800
-
- fix syntax err in alt
-In order there, we have:
-commits are made with the command git commit, but if we try to create a commit right now we'll be told "nothing to commit."
okay, what if we edit a file?
---tip: if you don't want to actually open your editor, just use
-touch file.txtto create a new empty file namedfile.txt
hmmm, there's still nothing to commit! What happens if we check git status? There's some new output!
Untracked files:
- (use "git add <file>..." to include in what will be committed)
- file.txt
-git tells us that we have "untracked" files - a.k.a. files that git hasn't got in its history yet. It also tells us to use git add if we want to be able to commit that file.
git has a concept called the staging environment or staging area. This captures the set of changes we're adding to a single commit. When we add something to the staging area, we say we are staging it or that it is staged. In order to stage changes, we'll use git add.
why a staging area? Why not just commit our changes?
-well, imagine we're writing a blog post (easy for me to imagine right now). We start reviewing it, and notice that there's a bit of page styling we don't like - not something tied to the content of the post, but the styling of the overall site. We fix it, and want to save that change while continuing to work on our post draft. git add and the staging area allow that kind of choice.
git add <filename> lets us add all changes in the given file to the staging area. Sometimes this is really useful - if we just created a new file (by, say, using touch file.txt), we probably want to add the whole thing.
personally, I really like using git add -p, so much so that I wrote an entire blog post about it. It lets us review changes piece-by-piece and pick only the pieces we want.
for now, we'll try git add file.txt. We'll notice there's no output by default, but we can run git status to see where things are at. git will now tell us:
Changes to be committed:
- (use "git rm --cached <file>..." to unstage)
- new file: file.txt
-now we're ready to create a commit!
-git commitif we just write git commit, it'll open an editor for us to edit the commit message - our description of the changes. This can be handy if we want to write a lengthy description, but if we want to just write a one-liner, we can use git commit -m "<message>". It's quicker and doesn't involve opening an editor.
let's create a super basic commit:
-git commit -m "baby's first commit"
---tip: as excited as you may be, don't use '!' in your commit messages
-
we'll see output like this:
-[main (root-commit) 3dcf1ca] baby's first commit
- 1 file changed, 1 insertion(+)
-trying git log now will show us our single commit!
--tip: type
-qto exit thegit logoutput
trying git status will tell us:
On branch main
-nothing to commit, working tree clean
-so, we've added a new file - that wasn't bad. Things get a little more interesting when we edit files git already knows about. Let's use <editor-command> . to open the current directory and write a sentence or two in file.txt.
after saving file.txt, try git status again.
--tip: Ctrl+S (or Cmd+S on Mac) is the shortcut for saving basically everywhere
-
Changes not staged for commit:
- (use "git add <file>..." to update what will be committed)
- (use "git restore <file>..." to discard changes in working directory)
- modified: file.txt
-git restoregit restore is new! That lets us get rid of our changes and go back to the last version of the file committed. Be careful with this - we should only do it if we really want to get rid of those changes.
let's not restore, and instead stage and commit our new changes:
-git add file.txt
-git commit -m "added a new sentence"
-again, we can use git status or git log as needed.
git revertooooh... I don't actually like that change. What if I want to undo something?
-run git log again, and copy the first 6-8 characters in the commit string (we can copy more, including the whole string if we want, but it's not necessary):
commit 8b5dd7838f8c8423cfa445b6cddbed88e9c32511 (HEAD -> main)
-Author: Lee Cattarin <lee.cattarin@gmail.com>
-Date: Wed Jan 7 15:18:45 2026 -0800
-
- added a new sentence
-
-in this case, 8b5dd7.
now we can try git revert <commit-string>. It'll open our editor to write a message about the change. It's important to know that git revert doesn't delete the old commit - it creates a new commit that undoes the previous work.
[main 9268d5c] Revert "added a new sentence"
- 1 file changed, 1 deletion(-)
-I didn't edit the message - we can tell because it just says "Revert" and then the old commit message. But we can edit and add lots of detail about why we're doing it.
-git remotelet's try a new command: git remote. Hmm, nothing happened... what's a "remote"?
remember how I said we could use git clone to work on an existing project? If we did that, we'd be getting that project from a remote server - not our local machine.
the world of git servers is vast - hell, you can run your own! - but we're going to just mention a few major hosts: GitHub, GitLab, and Codeberg. For this walkthrough, we're going to work with Codeberg, but you'll find that the UI is pretty similar across all three, so if you've got a GitHub or GitLab account feel free to use that.
-let's head on over to Codeberg First off, we'll make an account.
-now we'll make a new project using the + in the upper right. Choose 'New repository,' then pick a repository name. You can leave the other settings be.
with the project created, Codeberg will tell us three things we can do: clone the repository, create a new repository, or push an existing repository. We'll push an existing one.
-git remote add origin https://codeberg.org/inherentlee/testing.git
-git push -u origin main
-first, we'll add a remote. Across from the project title, we should see a button that says Code with a dropdown indicator. It'll offer a few choices, the first two being SSH and HTTPS. I'll talk about SSH in a bit, but let's try HTTPS first. Copy that URL; we're about to use it in a command.
--tip: the remote can be named whatever you want! Traditionally, it's called
-origin, but if it's easier for you to remember, you might call itcodebergor mayberemote
git remote add <remote-name> <url>
-for this walkthrough, we'll call our remote codeberg.
there's no feedback, but that's ok. Re-running git remote shows that we have a remote now: codeberg. That really doesn't tell us much, does it! Let's try a more talkative command: git remote --verbose or, more simply, git remote -v. Now it tells us the following:
codeberg https://codeberg.org/inherentlee/git-intro.git (fetch)
-codeberg https://codeberg.org/inherentlee/git-intro.git (push)
-cool! we have a remote set up. What does "fetch" and "push" mean?
-git fetch (and git pull)git fetch brings remote changes to our local machine. So does a command called git pull. Why are there two?
fetch brings the remote changes down, but doesn't combine them yet with our local work. This gives us a chance to explore what those changes are before we actually integrate them into our work!
this may seem unhelpful if we're thinking about this project as something only we work on, but imagine there's a team of people all contributing to the project. What if we and another person both work on the same file? Our changes might overlap!
-if we're working alone and from one machine, we'll pretty much never have to use git fetch or git pull! If we happen to do our work on multiple machines - for example, I do some work on my PC and some on my fruitpad (using an app called Working Copy) - we'll probably update the remote from one machine, then need to pull that work down onto the other machine.
for our use case, we can pretty safely stick to git pull (if we ever even need to use it!), but if you're working in a larger collaborative project, git fetch is your friend!
git push, take onegit push is the opposite of git pull - it takes your local changes and adds them to the remote.
the first time we use it on any given branch, we'll want to set what's called the upstream - the remote branch that our local branch is connected to by default. We can do this with the following command:
-git push --set-upstream codeberg main
-# or, for brevity
-git push -u codeberg main
-when we call git push, we're prompted for our Codeberg username and password.
personally, I find constantly authenticating tremendously annoying! There's a couple of ways to handle this.
-git config --global credential.helper cache will store our username and password in memory. You'll be re-prompted every 15 minutes. I work in long enough sessions that this is still a pain for me, but it may work for yougit config --global credential.helper store will save our username and password in a file on our machine, and only re-prompt if we change either value. Importantly, this method does not encrypt our password in any way! While it's convenient, it's not very securegit config --global credential.helper osxkeychain is a secure method for saving credentialsgit for Windows, we should have Git Credential Manager (GCM)while I'm not going to go into a lot of the technical concepts behind SSH keys, I will talk a bit about how my setup works. If you're happy with one of the other credential management setups above, feel free to skip past this section cause it's a bit chunky.
-the SSH (secure shell) protocol allows for secure communication on an insecure network.
-when we generate an SSH key, we get a public and a private key. These are mathematically related, and if we encrypt something with the private key, it can be decrypted with the public key, and vice versa.
-the important thing to know is you should never share your private key. Also, while you aren't forced to set a password when creating these keys, I strongly recommend doing so.
-running ssh-keygen will take us through a series of prompts. Assuming we don't already have an SSH key, the default file location is fine.
when you choose a passphrase, write it down. If you lose it, there is no recourse. You will have to generate a new SSH key.
-after generation, we will have two files at the location specified by the tool (or the custom location we chose). Generally, that's the folder .ssh in our home directory. If we navigate to that directory (cd $HOME/.ssh) and look at the files (ls), we'll see files named id_rsa and id_rsa.pub. The one that ends with .pub is the public key.
time for a little more terminal knowledge!
-I have two handy pieces of tooling in my terminal that I use for SSH operations.
-the first one is an alias - basically a simple shortcut for a command. I've written an alias for outputting my public key so that when I need it, I can get it without having to write out the path to the key. Laziness is a virtue, okay?
-there's a file in the home directory called .bashrc. It sets a lot of terminal-wide functionality. We're going to add an alias to it!
the command to output a file's contents is cat <filename>. My alias name of choice to cat my public SSH key is sshcat - but feel free to name yours something else.
navigate to the home directory (cd) and open your .bashrc file in your editor (<editor-command> .bashrc). Add the following to the bottom before saving and exiting.
alias sshcat="cat $HOME/.ssh/id_rsa.pub"
---tip: don't be alarmed if you can't use this right away! Your
-.bashrcfile takes effect when the terminal starts up. If you want to test it, either restart your terminal, or typesource $HOME/.bashrc
the other piece of shortcut SSH key tooling I use is a function that I call ssa, short for ssh-agent. ssh-agent manages SSH keys and keeps us logged in during a session.
# SSH agent
-ssa() {
- ssa_pid=$(pgrep ssh-agent)
- if [[ $ssa_pid ]]; then kill $ssa_pid; fi
-
- echo -n "$fg[green]"
- eval $(ssh-agent -s)
- ssh-add ~/.ssh/id_rsa
-}
-look, I'll be honest... we're not going to explain this one in detail. In short, though, it removes any existing instance of ssh-agent, then prompts us to put in our SSH key password so it can authenticate us.
open the .bashrc file again for editing, and add the above function to the bottom before saving and exiting. Again, either quit the terminal or type source $HOME/.bashrc to reload and use this new function by typing in ssa.
when we added a remote, we used the HTTPS URL. Let's update to using the SSH URL - you can find this on the main project page under the dropdown button that reads Code.
git remote set-url codeberg <new-url>
-we'll notice that the SSH URL starts with git@, whereas the HTTPS URL started with https://.
in order for all this to be useful, we need to tell Codeberg about our SSH key. In Codeberg, navigate to settings, then find the left-hand tab for SSH keys. Choose 'Add key' and paste in the public key (if you set up that sshcat alias, use it now to output your key for ease of copying). Save and we'll now be set up to authenticate with SSH!
git push, take twowe can now call git push again and again now without having to repeat our credentials every time. We can also call git pull for our private repositories.
let's talk about what we've done.
-git initgit statusgit log to view our commit historygit add to add new files or file changes to a commitgit commitgit revertgit remote to link that project to our local workgit fetch and git pullgit pushgit push!congratulations, and welcome to git!
-
-
- my therapist's idea
-7.5" x 4"
-default: black
-patch, print, sticker, shirt, card, pin
- -
-
-
- Listed by recency.
-
-
-
- Learn more about Artisans Cooperative, a new platform for makers and supporters.
-I've joined the "coop" and have made these cards/prints to support their fundraiser.
- - -
-
-
- Learn more about Artisans Cooperative, a new platform for makers and supporters.
-I've joined the "coop" and will be making shirts to support their fundraiser.
-Buy them on Artisans Cooperative's new marketplace.
-Buy them via my order form. (Sep 2024: No longer accepting orders via this method)
-
-
- did you know you can draw on (certain) mushrooms
- -
-
-
- Azure is Microsoft's cloud offering. Each possible resource that can be deployed in Azure has a location it's deployed in, such as "East US" or "Italy." While some resources can be deployed in all locations, other resources have location constraints.
-It's common, when deploying, to have a whole ecosystem of resources that will work together. However, this introduces a problem: which locations work for all resources in a deployment?
-Let's dig in. (Want just the outcome? Check the summary.)
-Bicep is a language for describing Azure resources. A Bicep file sets out a series of resources with preset or parameterized properties in order to deploy said resources.
-A minimal Bicep file that creates a resource group might look like this:
-param resourceGroupName string = 'myResourceGroup'
-param location string = "westus2"
-
-resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = {
- name: resourceGroupName
- location: location
-}
-This is easy to start parsing - I can use grep to find that Microsoft.Resources/resourceGroups@2022-09-01 string and go from there.
However, minimal is uncommon. As stated above, deployments of multiple resources are much more common.
-When working with large deployments, certain resources may be needed more than once. You can repeat your earlier storage account declaration, or, instead, you can template out how to deploy a storage account with given parameters, then reuse that template. This is called a module, and it's fundamental to organizing Bicep files.
Let's say this is our file structure. Ignore the lack of parameter files or READMEs, this is just an example.
-.
-|--infra
- |--env
- | |--dev
- | | |--main.bicep
- | |--prod
- | |--main.bicep
- |--modules
- |--rg
- | |--main.bicep
- |--vm
- | |--modules
- | | |--network.bicep
- | | |--virtual-machine.bicep
- | |--main.bicep
- |--kv
- |--modules
- | |--role-assignment.bicep
- | |--key-vault.bicep
- |--main.bicep
-Bicep files use relative references for local modules, so infra/env/dev/main.bicep references ../../modules/vm/main.bicep, which references ./modules/network.bicep. While the directory structure in this example could be flattened, my point is: modules can nest, and each module refers relatively to the module(s) it relies on.
Okay, let's backtrack. From a given Bicep file, we want:
-grepResources and modules both have patterns in how they are declared. Thankfully, they're pretty simple regexes. grep will spit out lines in a file that match a given regex.
# this gets us strings like
-# resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = {
-grep -E "^resource " "$file"
-
-# this gets us strings like
-# module vm '../../modules/vm/main.bicep' = {
-grep -E "^module " "$file"
-cutFrom there, let's use cut to strip off the parts we don't want.
# this gets us strings like
-# Microsoft.Resources/resourceGroups
-grep -E "^resource " "$file" \
- | cut -d "'" -f 2 - \
- | cut -d "@" -f 1 -
-
-# this gets us strings like
-# ../../modules/vm/main.bicep
-grep -E "^module " "$file" \
- | cut -d "'" -f 2 -
-These calls are a little opaque. -d sets a delimiter (what to split on). -f picks a field to return, numbered from 1.
mapfileWe'll save these values to variables. mapfile reads a file, putting each line into a new array element. -t trims newline characters. The <s do some redirection, and yes, the space between them matters.
mapfile -t resources < <(grep -E "^resource " "$file" \
- | cut -d "'" -f 2 - \
- | cut -d "@" -f 1 -)
-mapfile -t modules < <(grep -E "^module " "$file" \
- | cut -d "'" -f 2 -)
-dirname (& more)We can't just stop there. We need to search each module in turn. Using dirname, we can get the directory of the file we're searching, then append the relative module path.
get_resources () {
- # ... grep, cut, etc ...
-
- directory=$(dirname "$file")
-
- for module in "${modules[@]}"
- do
- mapfile -t -O "${#resources[@]}" resources < <(get_resources "$directory/$module")
- done
-}
-A lot just happened there besides dirname. {modules[@]} is all the array elements (as opposed to just $modules, which evaluates to the first element). ${#modules[@]}, on the other hand - note the pound sign - is the number of elements in the array.
Additionally, mapfile usually writes from index 0 onwards. But with the -O argument, we can specify an origin. By setting the starting point to the length of the array, we append to the array rather than writing over existing data.
Finally, we got some recursion going! get_resources calls get_resources for every module found.
get_resources functionSo far, our code looks like this:
-get_resources () {
- mapfile -t resources < <(grep -E "^resource " "$file" \
- | cut -d "'" -f 2 - \
- | cut -d "@" -f 1 -)
- mapfile -t modules < <(grep -E "^module " "$file" \
- | cut -d "'" -f 2 -)
-
- directory=$(dirname "$file")
-
- for module in "${modules[@]}"
- do
- mapfile -t -O "${#resources[@]}" resources < <(get_resources "$directory/$module")
- done
-
- for resource in "${resources[@]}"; do; echo "$resource"; done
-}
-That last one-liner just returns our results. Note that we don't just echo "${resources[@]}" - this results in a space-delimited string and it'll be helpful later to have a newline-delimited string.
Now we need to use these resource types to get available locations. First, actually call our function from above. We'll assume we're in a directory with a top-level main.bicep file.
mapfile -t resources < <(get_resources "main.bicep")
-sortDoes sorting matter? Not really, but sort has a useful feature, -u, which returns unique items (aka, it deduplicates). Looking up the same resource type twice slows us down.
mapfile -t resources < <(get_resources "main.bicep" | sort -u)
-sort is one reason it helps to have newlines as delimiters - it expects that.
azWe'll use az to list all the locations - just to give ourselves a starting point. You could also use the locations for the first resource type.
mapfile -t locations < <(az account list-locations --query "[].displayName" \
- --out tsv)
-We can then use an az command to find available locations for a given resource type:
mapfile -t newLocations < <(az provider show --namespace "$namespace" \
- --query "resourceTypes[?resourceType=='$resourceType'].locations | [0]" \
- --out tsv)
---out tsv means we will get a list with no decoration whatsoever - it's vital for programmatic handling of az command output.
cut (again)We'll need to get those $namespace and $resourceType variables. cut comes back in handy:
# remember, $resource is something like Microsoft.Resources/resourceGroups
-
-# this gets us strings like
-# Microsoft.Resources
-namespace=$(echo "$resource" | cut -d "/" -f 1 -)
-
-# this gets us strings like
-# resourceGroups
-resourceType=$(echo "$resource" | cut -d "/" -f 2 -)
-commOkay, we can get locations. How do we handle finding their intersection?
-comm to the rescue. It finds common lines between two sorted files. Its default output is three columns - lines only in file 1, lines only in file 2, and lines common to both. We can suppress the first two columns with -12.
comm expects files, so we'll reuse our redirection <(someCommand) from earlier.
mapfile -t locations < <(comm -12 \
- <(for location in "${locations[@]}"; do echo "$location"; done) \
- <(for location in "${newLocations[@]}"; do echo "$location"; done) )
-comm also likes newline-delimited input, so we're again looping through the array rather than echoing all values at once.
With functionality as it is, many deployments will come back with 0 locations available. Turns out some basic resource types, like role assignments, don't have locations. So let's filter those.
-if [[ ${#newLocations[@]} -eq 0 ]]
-then
- # handle
-fi
-teeWe'll print the locations to the shell. We can even use tee to print them to a file for good measure:
for location in "${locations[@]}"; do echo "$location"; done | tee locations.txt
-Here's our code for this section:
-mapfile -t resources < <(get_resources "main.bicep" | sort -u)
-
-mapfile -t locations < <(az account list-locations --query "[].displayName" \
- --out tsv)
-
-for resource in "${resources[@]}"
-do
- namespace=$(echo "$resource" | cut -d "/" -f 1 -)
- resourceType=$(echo "$resource" | cut -d "/" -f 2 -)
-
- mapfile -t newLocations < <(az provider show --namespace "$namespace" \
- --query "resourceTypes[?resourceType=='$resourceType'].locations | [0]" \
- --out tsv)
-
- if [[ ${#newLocations[@]} -eq 0 ]]
- then
- continue
- fi
-
- mapfile -t locations < <(comm -12 \
- <(for location in "${locations[@]}"; do echo "$location"; done) \
- <(for location in "${newLocations[@]}"; do echo "$location"; done) )
-done
-
-for location in "${locations[@]}"; do echo "$location"; done | tee locations.txt
-Here's our final script:
-# Recursively crawls bicep files to find all referenced resources
-get_resources () {
- mapfile -t resources < <(grep -E "^resource " "$file" \
- | cut -d "'" -f 2 - \
- | cut -d "@" -f 1 -)
- mapfile -t modules < <(grep -E "^module " "$file" \
- | cut -d "'" -f 2 -)
-
- directory=$(dirname "$file")
-
- for module in "${modules[@]}"
- do
- mapfile -t -O "${#resources[@]}" resources < <(get_resources "$directory/module")
- done
-
- for resource in "${resources[@]}"; do echo "$resource"; done
-}
-
-# Execution starts here
-mapfile -t resources < <(get_resources "main.bicep" | sort -u)
-
-mapfile -t locations < <(az account list-locations --query "[].displayName" \
- --out tsv)
-
-for resource in "${resources[@]}"
-do
- namespace=$(echo "$resource" | cut -d "/" -f 1 -)
- resourceType=$(echo "$resource" | cut -d "/" -f 2 -)
-
- mapfile -t newLocations < <(az provider show --namespace "$namespace" \
- --query "resourceTypes[?resourceType=='$resourceType'].locations | [0]" \
- --out tsv)
-
- if [[ ${#newLocations[@]} -eq 0 ]]
- then
- continue
- fi
-
- mapfile -t locations < <(comm -12 \
- <(for location in "${locations[@]}"; do echo "$location"; done) \
- <(for location in "${newLocations[@]}"; do echo "$location"; done) )
-done
-
-for location in "${locations[@]}"; do echo "$location"; done | tee locations.txt
-
-
-
-
- --These notes are from a talk I gave at work. If you think something is missing or incorrect, please let me know!
-
Backend developers still have users: other developers. We're all human, with human limitations and differences. Design for those limitations and you'll find you improve the end result for everyone.
-The first thing you can do: document. By this I don't just mean standalone text documentation; I also include code comments, clear variable and file naming, and pipeline or script outputs that report success or failure and give details.
-Rely on existing standards where possible. Style guides, spell checkers, linters, and formatters are all great. Make standards easy to follow with linter, editor, etc. config files in your repo, or add a devcontainer so others can easily get started with the project. When there's no standard, create one; for example, set up github issue and PR templates. And whenever you can, automate tests and fixes.
-Sighted users may take for granted the ability to skim a page by glancing at headers or highlights. Users with screen readers rely on several features for the same functionality.
-For example, screen readers can summarize the headers on a page. To leverage this, break up content with headers and avoid using other formatting to achieve similar effects visually. Stick to one h1 per page, and don't skip levels (e.g. go from an h1 to an h3).
-Screen readers can also summarize a page's links. In order for this to be effective, links need descriptive text attached to them (and always avoid bare links!). Compare these examples:
---Read more about accessibility patterns on the web
-
vs
---Read more about accessibility patterns on the web
-
The second example has text directly attached to the link that describes its content. This is a vast improvement over the first example, where the link would just be read out as "more". Since links are visually highlighted, good link text also improves readability for everyone.
-A table of contents can be helpful for a broad set of users, from the power user who knows exactly what she needs from the page to the newbie who just wants to see what the major topics are.
-Alt text/image descriptions for image and transcription/audio descriptions for videos are essential (and not just for screen readers - they're really useful if you've got poor internet connection). The references section of this document will link to more information on writing good alt text, but in general, focus on why the image/video is there and what it is conveying.
-(If you find you are simply transcribing text in an image, remove the image unless it conveys additional detail - images showing text intended to be read are less user-friendly than the same content conveyed as text. If the text is purely decorative and not intended to be read, carry on - just make sure you don't transcribe it since it's not meaningful!)
-I find this comes up the most in backend as diagrams. We love diagrams in place of words! Unfortunately, you'll want those words for some users eventually. Avoid alt text like "diagram of components" or "flow chart showing pipeline" - either write out a more direct explanation that mentions all entities contained in the diagram, or direct the reader to a section of text that covers the same content (e.g. "flow chart of the pipeline described below"). If you're writing a direct explanation, don't feel the need to describe each shape or arrow - focus on describing the relationships and entities those shapes and arrows represent.
-These are bits of feedback or further thoughts that have yet to be integrated into this.
-
-
-
- adjustable! also can be made with rainbow fittings.
- -
-
-
- Baseball stitched book!
-3" x 2" ish
-drawing paper
- -
-
-
- Hand carved stamp based on a photo of Jorts the cat.
-roughly 4.5" x 3.5"
-default: jalapeno
-patch, print, greeting card
- -
-
-
- Fiber from Woolgatherings. 70% Blue-Faced Leicester 30% tussah silk.
- -
-
-
- A large, large pigeon. Based on a fantastic photograph by Chris Price.
-5" x 5" or so
-default: black, sometimes with some blue, purple, green "iridescence"
-patch, print, greeting card, shirt
- -
-
-
-
-
-
- A two-tone blue and brown leather journal closed by a button. The inside front cover has a small pocket.
-
-
-
- cheeky lil congrats on the top surgery card :D
- -
-
-
- Celebration of testosterone-driven bottom growth. Submitted for T! the Zine.
- -
-
-
- Handstitched leather bowtie with standard clasp.
- -
-
-
- To order this as a shirt, please fill out the order form (Sep 2024: no longer taking orders via this method).
3" x 8"
-default: pink
-patch, print, greeting card, sticker, shirt, pin
- -
-
-
- 3/4" wide collar with stainless/nickel fittings.
- -
-
-
- To match brooke's collar.
- -
-
-
- A collage book full of different paper, art, and more.
-
-
-
-
-
-
- My second pair of socks, knit for my partner. They use Schoppel-Wolle Das Paar yarn in the colorway Fruhjahrsputz / Spring Cleaning. This yarn is designed to produce identical striping for each sock, and it very nearly did, with only a tiny discrepancy notable as I got to the toes.
- -
-
-
- for our 3 year anniversary :)
- -
-
-
- Created for Stanza 2024, a show highlighting nonbinary artists and poets.
-My art was inspired by the fantastic poem "A blurred arboreal" by Lore Kahuapāʻani.
- -
-
-
- This pattern is a Work in Progress and will be updated with more information as I make a second pair of these.
-This pattern was built on the Podster Gloves pattern by Glenna C. on Ravelry. I highly recommend reading and following that pattern to fill in gaps with this one.
-These gloves are built in two parts: bulky yarn section, then fingering. First, with bulky yarn and larger needles, the ribbed cuff is knit in the round. From the cuff we build the back of the hand by working a portion of the stitches flat in stockinette. The back of the hand then forms the mitten top by making additional stitches and joining to work in the round again, then knitting until a taper and grafting the ends with Kitchener stitch.
-With fingering yarn and smaller needles, we work from the leftover cuff stitches up to build the palm, using decreases to connect the edges of the bulky and fingering sections. A thumb gusset is built with increases, then put onto waste yarn to work later. When the palm is knit to the point it aligns with the start of the mitten top, we pick up stitches from the inside edge of the mitten top and join to knit the rest of the hand and fingers in the round. Finally, we split off fingers and work each separately, including returning to finish the thumb.
-With larger needles and bulky yarn, CO 28 (N) stitches and join to work in the round, placing a BOR marker. Work in k1p1 rib or your preferred rib until desired length of cuff is reached, then stop at BOR.
-Remove BOR marker. Turn to work WS.
-p15 (N/2 + 1) stitches. Place a locking stitch marker into the first and last stitches of this row for a reference point later. Turn to work RS.
-Back of hand repeat:
-Repeat until work sits just below the knuckles, ending with a RS row.
-Thread a piece of waste yarn through all back of hand stitches. This is used as a reference point later to pick up stitches for the hand.
-At the end of a RS row, place a BOR marker, then make 13 (N/2 -1) stitches using the backwards loop cast on or your preferred method. Join to work in the round.
-Establish a ribbed cuff on the palm side of the mitten top while leaving the back of the hand in stockinette:
-Repeat for 4 rows or your desired length.
-Knit in stockinette until work is about level with the end of the pinky finger.
-Mitten top decreases:
-Place first 7 stitches onto one needle and second 7 stitches on to another. Graft together using Kitchener stitch or your preferred method.
-You can alternately begin the decreases on the pinky side of the hand earlier than on the pointer side - this will make the mitten top fit the hand closer.
-You will now start to work with the other stitches left by the cuff, on the RS. Use smaller needles and fingering weight yarn.
-Start by making 1 from the yarn between the back of hand (BOH) stitches and your first stitch. Then kfb (or your preferred increase) across all remaining cuff stitches. Make 1 more stitch from the yarn between your stitches and the BOH. You should have 28 (N) stitches.
-At the end of your RS row, pick up the selvedge stich below the stitch we marked earlier. Then turn your work.
-k2tog - the selvedge stitch and your last stitch. purl across to the last stitch in the row, then ssk that stitch together with, again, the selvedge stitch below the marked stitch. Turn your work.
-Palm repeat:
-You only need to do this one or two times before starting the thumb gusset.
-I diverge slightly from the Podster Gloves pattern here in that there is only one line of increases going up this thumb, and so we increase more frequently.
-Follow right or left hand instructions accordingly until you have added 16-20 stitches depending on thumb size.
-If you would like to place a marker for the increases, I recommend setting that up as follows:
-and then on future RS:
-This ensures that you are not doing your make ones with a marker in the way. But I generally don't use the marker and just knit all the "new" stitches before doing the m1R.
-To be continued...
- -
-
-
- Hand carved stamp based on a photo of chanterelle mushrooms.
-about 2" square
-default: 2-tone yellow
-patch, print, greeting card
- -
-
-
- Fiber from Circle R Ranch. 100% alpaca, from Charlie the alpaca.
- -
-
-
- it's roumd
- -This is v2 of my personal website, built with Eleventy v3.1.2 and hosted by heckin' technology. It's been hand-coded from the ground up.
-v1 of this site began in 2022 and was based on Millennial, a minimalist Jekyll theme for running a blog or publication by Paul Le.
-The fonts are Atkinson Hyperlegible Next and Atkinson Hyperlegible Mono for standard text and monospace respectively, specifically designed for low-vision readers to improve character recognition. Also they look neat :)
-Thank you to some lovely friends for their feedback and help with the site! You should hire them. Yes, you.
- -You can find the accessibility statement here. If you'd like, you can view the site's palette or the style overview. You can also explore the sitemap.
-This site is created without the use of generative AI.
- -
-
-
- 3" x 5"
-default: black
-patch, print, greeting card
- -
-
-
- I'm fucking depressed. No, not like mental health depressed (okay, look, that too, but that's not relevant here). Looking to switch text editors, I reviewed 6 different options...and what I found didn't thrill me.
---this post contains comparison tables that are far more viewable on desktop/tablet
-
the editors I reviewed, in no particular order, are:
-I reviewed looking for 5 major functional qualities that I considered to be my most useful or heavily-used features:
-.editorconfig support3 less important, but preferred, aesthetic qualities:
-and finally, 3 ethical and trustworthiness qualities:
-(in other words: no LLMs).
-| - | .editorconfig |
-find-and-replace | -WSL | -multi-edit | -.md preview |
-
|---|---|---|---|---|---|
| VSCode | -yes[1] | -yes | -yes[1] | -yes | -yes | -
| Zed | -yes[1] | -yes | -yes | -yes | -yes | -
| Kate | -yes | -yes | -no[2] | -yes | -no | -
| Lapce | -no | -yes | -no[3] | -yes | -no | -
| Pulsar | -no | -yes | -no[3] | -yes | -yes | -
| Sublime Text | -yes | -yes | -no[3][4] | -yes | -no[5] | -
[1] extension needed: VSCode .editorconfig, VSCode WSL, Zed .editorconfig
[2] I could open a WSL directory in Kate, but couldn't see any files. I confirmed that opening a Windows directory worked as expected.
-[3] opening the project worked fine, but I couldn't delete files. In Sublime Text's case, they were deleted but still shown in the file view.
-[4] saving a new file opens the save menu in the Windows File Explorer, which frankly makes me a bit afraid. Touching your WSL files from Windows is generally a bad idea.
-[5] I explored two different add-on packages for Markdown preview support. Markdown Live Preview and MarkdownPreview. Markdown Live Preview opened a whole new window scoped only to the specific .md file. MarkdownPreview previewed in browser. Neither of these match the behavior I am looking for.
| - | UI | -file icons | -color scheme | -
|---|---|---|---|
| VSCode | -yes | -yes | -yes[1] | -
| Zed | -yes | -yes | -yes[2] | -
| Kate | -no | -yes | -yes[2] | -
| Lapce | -yes | -no | -yes[3] | -
| Pulsar | -yes | -no | -yes[1] | -
| Sublime Text | -yes | -yes | -yes | -
[1] several color schemes available. Further extensions available.
-[2] several color schemes available.
-[3] only light and dark schemes available. Further plugins available.
-this is my best guess based on searching online and reviewing the settings; it's kind of hard to really confirm these things. I am judging the last quality - whether or not the devs are using LLMs - based on the existence of prompts directories in the projects' repositories.
| - | no LLM features | -LLM kill switch | -no LLM code | -
|---|---|---|---|
| VSCode | -no | -no | -no[1] | -
| Zed | -no | -yes | -no[2] | -
| Kate | -yes | -n/a | -yes | -
| Lapce | -yes | -n/a | -yes | -
| Pulsar | -yes | -n/a | -yes[3] | -
| Sublime Text | -yes | -n/a | -unknown[4] | -
[2] looks like at current LLMs are only used for documentation
-[3] it's been discussed and sounds currently up for debate: Pulsar, Sublime Text
-[4] I couldn't find the source code for Sublime Text online; I assume it's not OSS. If you know where it is, point me in that direction.
-I'll be honest, I just don't know. The functionality is not something I can easily compromise on. TBH, I figured I had pretty basic needs as a developer, but it seems that's not the case! The only editors that meet my functionality needs across the board are also the worst offenders on the LLM front.
-at the end of the day, I might just have to keep looking... but regardless, I wanted to publish what I found to help anyone else with similar needs.
-.editorconfig support requires an extensionI learned that WSL2 can support GUI apps so I tried this for a few. Pulsar did not work; Sublime Text worked but the UI scale was teensy and was not affected by the ui_scale setting.
-
-
- Hand carved modular stamps to congratulate your friend or yourself on your fantastic neurodivergence.
-As these are modular, I can expand the range of terms offered if there is demand for it.
-roughly 4" x 3"
-default: onyx black
-patch, print, greeting card
- -
-
-
- A variation on my congrats on the autism/adhd cards.
-Hand carved modular stamps to congratulate your friend or yourself on your fantastic gayness.
-As these are modular, I can expand the range of terms offered if there is demand for it.
-roughly 4" x 3"
-default: onyx black with rainbow lettering on 'Gay!'
-print, greeting card
- -if pricing is an issue for you, reach out and we can work out sliding scale options - or an art trade!
-Find me at various art shows and fairs.
-
-
-
- Fiber from Jakira Farms in Coral Reef colorway. 100% merino.
- -
-
-
-
-
-
-
- the first step in any design is small tests. For leatherwork, this usually means working with paper - cheaper than leather, but with some of the same inflexibility.
-my grommets, laid out on paper, spaced out cleanly at 1/2" away from edges and 1" apart. My tablet is ~10"x8". I started with these facts.
-don't measure out the same spacing any more than you have to. Templating is your friend for both ease of use and regularity.
-the corners should be stitched before lacing in order to get into tight spaces easily. Grommets are set most easily before the work is 3-dimensional. It's cleanest to cut before grommets are installed so as to work on a smooth and level surface. Hold all this in your mind when choosing your next move, and ensure you don't step on your own toes.
-even if you won't be making one, what would you do differently? Where does your design fail? Where does it succeed?
-easier said than done.
- -
-
-
- This was written as a skill share for the Artisans Cooperative blog.
-In this writeup, I will walk through one (of many!) ways to set up and utilize a custom domain and website.
-This is aimed at a non-technical audience, but my own perspective is technical and some of the articles I link to will get technical. Because of this, it's possible that I will miss things that you have questions on - please reach out to me and ask questions!
-After following this writeup, you should have:
-This writeup will not cover every possible route to getting your own home on the web - there's far too many options out there. It's just meant to give you a starting point and a few ideas.
-I started planning my domain name by reviewing the list of TLDs - things like ".org" or ".com". ICANN (the Internet Corporation for Assigned Names and Numbers) maintains a list of all TLDs - it's long! Reviewing this list can help you think of potential domain names. You can also look at a list like this one Wikipedia maintains - it has more detail and can tell you if a specific TLD is for a given country, reserved for specific uses, or (what you probably want) for general use.
-Personally, I narrowed it down to two - ".art" or ".gay". From there, it's off to the domain registrars! These are companies that offer domain names for sale. Here's an article from Forbes Advisor reviewing some of the major names in the space. I use Namecheap, but don't let that bias you. Look around at pricing (pay attention to the rate for year 2 onward - domain registrars often offer super low year 1 rates to hook customers) and consider other features, like support availability.
-Narrowing down a TLD isn't the only choice - you also need to decide what goes in front of that! Some things to think about:
-In my case, I went with leecat.art. The ".art" TLD was cheaper than the ".gay" option long-term, and I shortened my full, somewhat hard to spell name to a quick two syllables, 3 characters each.
-Pick a domain registrar that offers the domain name you want, and pay (usually for a year). Next, we'll talk about some uses for this domain name.
-The easiest way to utilize a domain name is to have it redirect to another URL. I'm not going to go through how to set up a domain redirect with every possible provider, but if you search up "[your domain registrar] redirect" you should find useful documentation.
-Link trees (popularized by linktree) are a single page with a collection of links maintained by an owner. You can use linktree or check out this WIRED article with alternatives. Some are paid, usually small monthly fees, and some are free.
-GitHub - wait, no.
-First, let's talk about git. Git is a version control system, a type of software that manages changes to a set of files. This allows the owner(s) of those files to do things like revert changes or compare current and historical versions of files. It also allows for multiple people to work on the same shared file repository without creating conflicts.
-GitHub is a centralized source for many, many git repositories. It essentially allows you to back up both your code and the log of all changes to the cloud (someone else's computer). It also supports GitHub Pages, a free way to host a static site of your own.
-To get started with GitHub, you'll first need to create an account if you don't have one. If you want to learn some GitHub basics, the GitHub team has created this handy introduction to GitHub that walks you through some basic git and GitHub concepts.
-The GitHub documentation is pretty thorough, so let's point to some articles over there.
-
-
-
- continuing to iterate on bifolds. made of a variety of discount leather with various imperfections. fully hand-stitched.
- -
-
-
- pattern by PaintYee
- -
-
-
- hand-dyed with acid dyes
- -
-
-
-
-
-
- a slight change to the first wallet design. made of a variety of discount leather with various imperfections. fully hand-stitched.
- -
-
-
- recently I wrote several sites using Eleventy (4? 5?). Including, over the past few days, rewriting this one! That's right, if you're reading this, we're now running on 11ty and hosted by heckin.technology. See ya, GitHub. Won't miss ya.
-Originally, I compiled some of the things I've learned in a standalone site.
-however, since I don't know how much I'll focus on that specific site - it is mostly a sample - I am re-publishing the most useful information here. I'll skip the intro to Markdown content. I'm also going to update them where I've learned more or to better match what's represented on this site.
-this will comprise of 4 parts: related posts, featured images, pagination, and tag image preview. Feel free to jump ahead, as none depend on the others.
-by default, the Eleventy base blog comes with pagination between posts. Post 2 can take you to posts 1 and 3, etc.
-while that is useful for this site, when building another site I wanted to see a couple randomly-suggested posts that shared 1 or more tags.
-I started by referring to this GitHub issue about related posts. I had to fix a few errors that arose from the suggested code.
-I also wanted to make three changes:
-after adjusting for those needs, I had the following in filters.js:
eleventyConfig.addFilter("excludeFromCollection", function (collection=[], urls=[this.ctx.page.url]) {
- return collection.filter(post => urls.indexOf(post.url) === -1);
-});
-
-eleventyConfig.addFilter("filterByTags", function(collection=[], ...requiredTags) {
- return collection.filter(post => {
- return requiredTags.flat().some(tag => post.data.tags.includes(tag));
- });
-});
-
-eleventyConfig.addFilter("randomize", function(array) {
- // Create a copy of the array to avoid modifying the original
- let shuffledArray = array.slice();
-
- // Fisher-Yates shuffle algorithm
- for (let i = shuffledArray.length - 1; i > 0; i--) {
- const j = Math.floor(Math.random() * (i + 1));
- [shuffledArray[i], shuffledArray[j]] = [shuffledArray[j], shuffledArray[i]];
- }
-
- return shuffledArray;
-});
-I used this in my post layout. filterTagList comes with the base blog by default, and removes the tags "posts" and "all." head also comes with the base blog. postlist.njk is my modified-from-the-base-blog post layout.
{% set relevantTags = tags | filterTagList %}
-
-{% set olderPost = collections.posts | getPreviousCollectionItem %}
-{% set newerPost = collections.posts | getNextCollectionItem %}
-{% set urlsToExclude = [page.url, olderPost.url, newerPost.url]}
-
-{% set postlist = collections.posts | filterByTags(relevantTags) | excludeFromCollection(urlsToExclude) | randomize | head(3) %}
-{% if postlist.length %}
-<section class="related-posts">
- <h2>related posts</h2>
- {% include "postlist.njk" %}
-</section>
-{% endif %}
-and that sorts related posts.
-images in 11ty use the Image Transform Plugin. I found it hard to find anything to reference while building this - a lot of sites in the template gallery are either text-heavy or not using the plugin - so I'm reproducing what I've got here for reference.
-content/
-|--img/
-| |--sample-0.jpg
-| |--sample-1.jpg
-| `--etc
-|--posts/
-| |--lesson-0-front-matter-and-urls.md
-| |--lesson-1-headings-paragraphs-and-horizontal-lines.md
-| `--etc
-`--etc
-
-for any given post, my front matter references the image in this manner:
----
-image:
- src: sample-0.jpg
- alt: moss on a fencepost
----
-
-As mentioned, there's a plugin for images. If you started with the base blog, in eleventy.config.js, you'll probably find a chunk of code similar to this already in place:
eleventyConfig.addPlugin(eleventyImageTransformPlugin, {
- formats: ["auto"],
-
- widths: [640],
- failOnError: true,
- htmlOptions: {
- imgAttributes: {
- // e.g. <img loading decoding> assigned on the HTML tag will override these values.
- loading: "lazy",
- decoding: "async",
- }
- },
-
- sharpOptions: {
- animated: true,
- },
-});
-setting formats to "auto" helps - use whatever type of image you want, get that type out. The default settings that came with the Eleventy base blog didn't set a width, which I wanted (by default, images off my camera - like the hellebore featured image for this post - are almost 5k pixels wide). I also found it helpful to set failOnError to true for a little more feedback.
--NOTE: It sure seems like Eleventy will fail your image processing if there's no alt text. While admirable, it would be nice if I could find any documentation for this!
-
as I worked through this, I thought I needed a passthrough copy for a while. You don't - just let the plugin do its thing.
-I needed images to show up in 3 places:
-both of these sections use the same template, which in my setup is called postlist.njk. Within the relevant links, I added the following:
{% if post.data.image.src %}
- <img src="/img/{{ post.data.image.src }}" alt="{{ post.data.image.alt }}">
-{% else %}
- <div class="missing-image"></div>
-{% endif %}
-the post body looks similar:
-{% if image.src %}
-<img class="featured-img" src="/posts/img/{{ image.src }}" alt="{{ image.alt }}">
-{% endif %}
-together, that sets up featured images for posts.
-Post pagination in Eleventy is pretty straightforward, mostly requiring some specific front matter.
-The home page pagination I have set up here looks like the following (in index.njk):
---
-pagination:
- data: collections.posts
- size: 13
- alias: posts
- reverse: true
----
-
-6 posts per page, paginate data from collections.posts which we'll call just posts for short, and do it in reverse (aka, most recent posts show first).
You'll also likely want previous and next buttons. I did. Here's what I have...
-There's two components to this, the bigger one being this pagination.njk template. Look, I like my little icons, ok? It takes an olderHref and a newerHref, and optionally an olderTitle and newerTitle.
{% if olderHref or newerHref %}
-<nav aria-label="pagination">
- <ol class="pagination {% if inPost %}post-pagination{% endif %}">
- {% if olderHref %}
- <li class="older">
- <a href="{{ olderHref }}">
- <i class="fa-solid fa-hand-point-left" aria-hidden="true"></i>
- {{ olderTitle or "older" }}
- </a>
- </li>
- {% endif %}
- {% if newerHref %}
- <li class="newer">
- <a href="{{ newerHref }}">
- {{ newerTitle or "newer" }}
- <i class="fa-solid fa-hand-point-right" aria-hidden="true"></i>
- </a>
- </li>
- {% endif %}
- </ol>
-</nav>
-{% endif %}
-From index.njk we can include "pagination.njk":
{# idk why these are backwards either #}
-{% set newerHref = pagination.href.previous %}
-{% set olderHref = pagination.href.next %}
-{% include "pagination.njk" %}
-
-Yup. The order of previous vs. next is totally unintuitive to me, too.
-however, there's a catch. Tag pages are created via pagination! It's a lot harder to paginate those - you can't just use the front matter to set it up.
-I untangled this GitHub issue about double-layered pagination and came to the following solution...
-in eleventy.config.js:
// note that this uses the lodash.chunk method, so you’ll have to import that
-eleventyConfig.addCollection("tagPagination", function(collection) {
- // Get unique list of tags
- let tagSet = new Set(collection.getAllSorted().flatMap((post) => post.data.tags || []));
- // Remove "structural" tags
- tagSet = tagSet.difference(new Set(["posts"]));
-
- // Get each item that matches the tag
- let paginationSize = 6;
- let tagMap = [];
- let tagArray = [...tagSet];
-
- for( let tagName of tagArray) {
- let tagItems = collection.getFilteredByTag(tagName);
- let pagedItems = chunk(tagItems.reverse(), paginationSize);
-
- for( let pageNumber = 0, max = pagedItems.length; pageNumber < max; pageNumber++) {
- tagMap.push({
- tagName: tagName,
- pageNumber: pageNumber,
- pageSize: pagedItems.length,
- pageData: pagedItems[pageNumber]
- });
- }
- }
-
- return tagMap;
-});
-in my tag-pages.njk file (or whatever you use to template out your tag pages):
---
-pagination:
- data: collections.tagPagination
- size: 1
- alias: tag
-eleventyComputed:
- permalink: /tags/{{ tag.tagName | slugify }}/% if tag.pageNumber %{{ tag.pageNumber + 1 }}/% endif %
- title: "Posts tagged {{ tag.tagName }}"
-eleventyExcludeFromCollections: true
----
-<h1>Posts tagged “{{ tag.tagName }}”</h1>
-
-{% set postlist = tag.pageData %}
-{% include "postlist.njk" %}
-
-{# idk why these are backwards either #}
-{% if tag.pageNumber > 0 %}
- {% set newerHref = pagination.href.previous %}
-{% endif %}
-{% if tag.pageNumber < tag.pageSize - 1 %}
- {% set olderHref = pagination.href.next %}
-{% endif %}
-{% include "pagination.njk" %}
-note the pagination checking tag.pageNumber against tag.PageSize - the original suggested solution in the GitHub post creates an issue where the pagination loops through all of the tag pages bit-by-bit. This sorts that - hat tip to TheReyzar who mentioned the issue and showed part of their solution.
finally, in my filters.js file, I add the tagPagination tag to the tags that get filtered using filterTagList:
eleventyConfig.addFilter("filterTagList", function filterTagList(tags) {
- return (tags || []).filter(tag => ["all", "posts", "tagPagination"].indexOf(tag) === -1);
-});
-today I tackled making the tag page more visually interesting.
-first, I worked on previewing the first featured image. The focus here is on digging into collections[tag] (reversed!) to get to the data of the first post.
<ul class="taglist">
-{% for tag in collections | getKeys | filterTagList | sortAlphabetically %}
- {% set tagUrl %}/tags/{{ tag | slugify }}/{% endset %}
- <li>
- <a href="{{ tagUrl }}" class="taglist-link">
- {% set tagRecent = collections[tag] | reverse %}
- {% if tagRecent[0].data.image.src %}
- <img src="/posts/img/{{ tagRecent[0].data.image.src }}" alt="{{ tagRecent[0].data.image.alt }}">
- {% else %}
- <div class="missing-image"></div>
- {% endif %}
- <span class="post-tag">{{ tag }}</span>
- {% set numPosts = collections[tag].length %}
- ({{ numPosts }} post{% if numPosts > 1 %}s{% endif %})
- </a>
- </li>
-{% endfor %}
-</ul>
-I found that having just the first featured image made the tag page hard to differentiate from any of the pages listing individual posts, so from there I moved to showing 4 images (or empty rectangles where there weren't four to show).
-<ul class="taglist">
-{% for tag in collections | getKeys | filterTagList | sortAlphabetically %}
- {% set tagUrl %}/tags/{{ tag | slugify }}/{% endset %}
- <li>
- <a href="{{ tagUrl }}" class="taglist-link">
- <div class="tag-imgs">
- {% set tagRecent = collections[tag] | reverse %}
- {% for i in range(0, 4) %}
- {% if tagRecent[i].data.image.src %}
- <img src="/posts/img/{{ tagRecent[i].data.image.src }}" alt="{{ tagRecent[i].data.image.alt }}">
- {% else %}
- <div class="missing-image"></div>
- {% endif %}
- {% endfor %}
- </div>
- <span class="post-tag">{{ tag }}</span>
- {% set numPosts = collections[tag].length %}
- ({{ numPosts }} post{% if numPosts > 1 %}s{% endif %})
- </a>
- </li>
-{% endfor %}
-</ul>
-with a bit of display: grid, we're good to go, right?
this had worked so far because the photos on the lessons site are from my camera in landscape mode, producing neat, identical 3:2 aspect ratios. Let's throw a wrench in that and introduce a portrait-mode photo.
-thankfully, the CSS property aspect-ratio makes this pretty straightforward, and object-fit finishes the job.
.taglist-link img {
- aspect-ratio: 3 / 2;
- object-fit: cover;
-}
-(I also set the missing-img <div>s to have the same aspect ratio.)
There's the good stuff from 11ty Lessons. Hope you enjoyed.
- -
-
-
- I am euphorbic.
- -
-
-
- A friendly neighborhood visitor.
-5" x 6"
-default: black
-patch, print, greeting card, shirt
- -
-
-
- This is in response to an F.D Signifier video, How to get RICH off weak men! (go follow him!). It is written as a response video and the script has not been changed to fit this format.
-As much as it's a response video, I think the content should also stand on its own.
-Recently I watched an FD Signifier video that I'll link in the description, currently titled "How to get rich off weak men"
-Now, overall, fucking excellent, you're great FD, you're very much someone I look up to. So let's ground things by stating I don't say any of this to disagree or denigrate any of your points, but rather to build on them.
-what we're going to start and end with today is a concept called oppositional sexism. This comes from Julia Serano writing in Whipping Girl.
-In addition to traditional sexism, where men are better than women and masculinity better than femininity, Serano argues that we also experience and propagate oppositional sexism, where men are seen as women's opposites. from Whipping Girl:
---the belief that female and male are rigid, mutually exclusive categories, each possessing a unique and nonoverlapping set of attributes, aptitudes, abilities, and desires
-
oppositional sexism positions men as opposed to women, rather than understanding both groups as heavily overlapping in characteristics, abilities, interests, physicality, etc.
-where analysis based in traditional sexism takes the gender binary as fact and analyzes how we move within it, oppositional sexism takes aim at the gender binary itself and looks at the ways that it is constructed.
-now what does this have to do with FD's video? Well, I would characterize his analysis as grounded in, among many other things, an understanding of traditional sexism. He talks about the ways boys learn about maleness and masculinity and its dominence over femaleness and femininity, but never questions the existence of the category "boy" in opposition to "girl".
-And I think we have to! we can't just examine misogyny and toxic masculinity while unconsciously accepting the creation of binary gender, because the creation of and pressure on binary gender helps create misogyny and toxic masculinity. When boys are continuously told they are wildly different from girls, when they are continuously split up and separated, when girls are portrayed as almost a different species, we invite unhealthy attitudes about women and girls.
-this is of course hypothetical - we live in a society and can't escape it - but if instead we stressed that all children are human, that they are part of a unified group, i can't help but imagine that we would have much healthier attitudes towards sex and dating.
-now, obviously, the deconstruction of misogyny is enough reason to use this for analysis. we want to minimize misogyny and toxic masculinity, and this supports that goal. but let's also talk about the way bioessentialism and binary gender roles foster other biases.
-transphobia and particularly anti-nonbinary sentiments are probably the most obvious ones propped up by this, but there's also:
-homophobia or biphobia: when we view men and women as two discrete and opposing groups, there's a large difference between a male-female relationship and a male-male or female-female one.
-intersex erasure and discrimination: the coercive and corrective sexing of intersex infants, up to and including nonconsensual surgical modification, should be the obvious example, but let's also consider the ways that intersex people are erased by the gender binary. Intersex people are not uncommon, with various estimates ranging from one in two thousand to one in about sixty. But the structure of the gender binary must insist that they are essentially one in a million. it makes no room for them - they must be outliers.
-and, interestingly,
-Now, I want to be clear: I am not claiming that misogyny or the gender binary is the root of all other biases. I think the world is far more complex than that. But I do believe that the gender binary, and more broadly, a binarist mindset, is a fertile breeding ground for other biases.
-going forward, I want you to have the concept of oppositional sexism in your analysis toolbox. I want you to think critically about the way that the gender binary is not a natural outgrowth of humanity, but rather created and enforced. And I want you to question and push back against the way that you - as a man or woman - see yourself in opposition to, rather than in alliance with, women or men (nonbinary people, you get a pass here, I figure you're already thinking about this).
- -
-
-
- My new project FediLearns is a learning-focused classifieds page for the fediverse. Check it out!
- -
-
-
- FediZineFest returns for its second year. Read more about it and sign up on the fediZineFest 2025 website.
- -
-
-
- Fiber from Jakira Farms in Fire & Ice colorway. 100% merino.
- -
-
-
- Available for lots of flags! Except the lesbian sunset flag because it's really hard to color match :(
- -
-
-
- Photographed in the backyard.
-6" x 2.5"
-default: green and brown
-patch, print, greeting card, shirt
- -
-
-
-
-
-
-
- Hand carved stamp of a slightly goofy looking flatfish.
-about 2" around
-default: sepia
-patch, print, greeting card
- -
-
-
- Hand carved set of stamps of a northern flicker, hungrily eyeing our bird feeder.
-roughly 6" square
-default: black, sepia brown, and satin red
-print, greeting card, patch, shirt
- -
-
-
- A landscape-oriented notebook with black paper and a fuzzy cover. yes. it's fuzzy.
-~ 4" x 6"
-Designed for standard credit cards, not business cards.
- -
-
-
- Single piece construction for easy care - just unfold/pull out the tabs!
- -
-
-
- Wax seals pictured from boygirlparty.
-
-
-
- 6" x 6"
-default: green and pink
-patch, print, greeting card, shirt
- -the gallery page is for finished art
-(or browse by tags)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- the gallery page is for finished art
-(or browse by tags)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- the gallery page is for finished art
-(or browse by tags)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- the gallery page is for finished art
-(or browse by tags)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- the gallery page is for finished art
-(or browse by tags)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- the gallery page is for finished art
-(or browse by tags)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- the gallery page is for finished art
-(or browse by tags)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- the gallery page is for finished art
-(or browse by tags)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- the gallery page is for finished art
-(or browse by tags)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- the gallery page is for finished art
-(or browse by tags)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- the gallery page is for finished art
-(or browse by tags)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- the gallery page is for finished art
-(or browse by tags)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Combination of trans wrongs skull and the geese.
-6" x 4"
-default: black
-patch, print, shirt
- -
-
-
- Based on a fantastic picture by handmade ghost
-6" x 3"
-default: black
-patch, print, greeting card, shirt
- -
-
-
-
-The below material is a web page reproduction of my zine Gender as a Proxy Variable.
-Authored by Lee Cattarin
-August - December 2023 -> February 2024
---I would argue that you almost never have to ask for gender…
-If you’re collecting gender identity data to personalize user-facing copy, try asking for preferred* pronouns instead. If you’re asking because you want to make in-app content recommendations, try asking about the user’s content preferences. If you’re asking to generate a user avatar, let the user generate their own. Gender identity is a poor proxy variable — stick to asking for the information you actually want.
-
-- Nikki Stevens, quoted in “How to Make Your Software More Trans-Inclusive,” emphasis mine
-*pro tip: just say "pronouns" and drop the "preferred"
-So, what are you asking for when you ask for
-(this section and its effects have been altered to be functional in this medium - in the print zine, checkboxes and radio buttons were used. But Markdown only has one type of list.)
-Questions that require users pick a single answer will be followed with "(pick one)".
-Questions that allow users to choose multiple answers will be followed with "(pick any)".
-Write-in fields may have italicized suggestions in them.
-What pronouns should we use for you? (pick any)
-Worried about parsing that free text field? Try:
-What title or prefix should we use for you? (pick one)
-(et cetera)
-Do you require private facilities for breastfeeding or other health and wellness needs? (pick one)
-Provide a write-in space for specific needs, such as refrigeration or running water.
-Which style of shirt would you prefer? (pick one)
-Do you require any of the following restrooms? (pick any)
-All restrooms should be provided with menstrual products.
-What is your gender? This information will be viewable on your profile by all logged-in users. (pick any)
-Make sure to note the visibility level!
-What is your gender? This information is collected for demographic analysis only. (pick one)
-What is your legal sex as marked on government-issued identification? (pick one)
-Explain where this information will be used. If you need something more specific than any legal identification, say so: "This is used for insurance purposes and must match your gender on insurance paperwork."
-What is your gender? (pick any)
-What sex were you assigned at birth? (pick one)
-An organ inventory and/or surgical history is a useful tool.
-But let’s talk more about that
-Is:
-enough?
-Maybe not! Probably not, in fact! But what are our other options?
-Having a few primary options and a write-in, as shown above, is a good balance!
-A little more on
-Consider the following options:
-Aside from the lack of nonbinary gender choices, did you notice that?
-These exclusive choices present “man” and “trans man” (and “woman” and “trans woman”) as separate genders, when what they’re most likely trying to convey is:
-Avoid treating “cis” as the unspoken default.
-Other anti-patterns include:
-Here’s some additional positive patterns that can be appended to many of the earlier examples:
-This zine draws from material I gathered for a longer blog post: leecat.art/gender-in-data
-Linked in that blog post are numerous sources; the most heavily relied on here was from Drupal’s documentation and is found under the section headed “Do you need gender data?”
-Lee Cattarin is a transgender software developer and artist based out of Vashon, WA, USA. All hir creative work can be found at leecat.art
-Get in touch with hir via any of the methods listed on leecat.art/contact
-
-
-
-
-As transgender and nonbinary people gain visibility and legal identification expands gender fields to include nonbinary genders and/or "X" markers, data models that only support "male" and "female" (or "M"/"F") are no longer sufficient to align with individuals' legal records or personal identity. However, with the large range of terminology used by transgender and nonbinary people, tackling this problem can seem challenging and brings the potential to further harm or exclude already marginalized individuals.
-Proofreading and commentary from all readers!
-If you're knowledgeable on this topic, additional information and viewpoints are tremendously useful.
-If you're new, where do you have questions? What hasn't been explained enough? What do you leave this document still wondering about?
-This document will look at existing and potential solutions to modeling gender in data for use by software systems, primarily via presenting and analyzing patterns and anti-patterns.
-In order to more easily discuss these topics, I will note here my working definitions for some terms below.
---A note on language… Please do not assume that these definitions are fixed or can be broadly applied across cultures and countries. The language around gender has continued to change as the trans community is more able to connect, have in depth conversations, and define for ourselves how we use language. On an individual, person-to-person level, it's always preferable to mirror the language people use for themselves rather than prescriptively apply terms.
-
--Language note: trans and cis are derived from Latin, meaning "on the other side of" and "on this side of" respectively.
-
There's a dearth of data on trans individuals. That said, we do have some relevant sources that have given us broad insight into trans communities.
-Both of these sources use ‘nonbinary’ as their usual spelling; I am mirroring that here.
-It's always worthwhile to stop and look at how relevant or necessary gender information is to your goals.
--- -"'I would argue that you almost never have to ask for gender,' Stevens said.
-"If you’re collecting gender identity data to personalize user-facing copy, try asking for preferred pronouns instead. If you’re asking because you want to make in-app content recommendations, try asking about the user’s content preferences. If you’re asking to generate a user avatar, let the user generate their own. Gender identity is a poor proxy variable — stick to asking for the information you actually want.
-
Drupal's gender field includes the following guidance in its description:
--- --
-- Do you need to address a person with pronouns? Genders do not necessarily map easily onto the pronouns a person uses. If you need to associate pronouns with a person, ask for those pronouns directly.
-- Do you need to address a person with a title or prefix (such as Mr./Ms./Mx.)? Genders also do not necessarily correspond to a person's preferred title, and moreover would leave out honorifics related to profession, such as Dr., Rev., or Capt. If you need to associate titles with a person, ask for those titles directly.
-- Do you need to collect gender information for demographic data reasons? If you do, make sure you are able to accurately record a person's gender, rather than forcing them into choosing from limited options. If you need to use the data for recording trends, writing reports, or segmentation for advertising or other reasons, consider post-processing the data to group related genders depending on your specific use-case.
-- Do you need to know a person's health needs, clothing preferences or bathroom use? If you are organizing an event, for instance, you might want to know what sort of facilities to provide or what sorts of t-shirts to order. Genders, however, do not necessarily correspond to specific body types, body functions, health requirements (such as menstrual supplies) or reflect what types of facilities a person would feel safest using in a public environment. If you are collecting gender data for this purpose, ask the more precise questions specifically.
-- Do you want to publicly display a person's gender on a profile? This is often a choice made by social media and dating/relationship sites. If you do this, consider making the field optional altogether. If you are providing user avatars, remember that human bodies come in all sorts, and allow individuals to choose an avatar separately from collecting this data.
-- Do you need to know assigned gender for legal, medical or regulatory reasons? Current gender does not necessarily correspond to assigned sex at birth or legal gender marker, so be sure you are clear in what you are requesting of a person. It's particularly critical to be transparent about your privacy policy and the how this data will be used.
-
-- -....if this information is displayed publicly there is potential for abuse by people who like to make discriminatory jokes about gender identity, and any such system would need to put steps in place to prevent such abuse.
-...
-Instead of putting the burden on a user to fully understand the risks of sharing their highly personal information, let’s put the burden on ourselves to treat that information right. If we have no strong reason to collect it, or can’t guarantee its safety, we shouldn’t collect it.
-
If you do collect gender data, inform the user what it is used for and who it will be shared with.
-The availability of nonbinary gender options in legal systems varies by location. The ability to change one's gender marker (from one binary category to another or from binary to nonbinary) varies even more widely, with requirements ranging from simple voluntary declaration, to verification by medical professional(s) of varying treatments which vary in how accessible they are and whether they are actually desired by individuals for whom this is relevant, to only changing in response to proven "error," etc.
-(Transgender Law Center, 2017) (Movement Advancement Project) (Knight and Ghosal, 2016)
-If you are not specifically trying to refer to an individual’s legal identification, don’t tie gender to it.
-This is variously defined to refer to sex assigned at birth, legal gender/sex markers, or current physical sex based on any number of characteristics, which may include
-and more. This identifier may or may not be treated as binary and may or may not take into account an individual's medical history.
- -Outside of healthcare, this is largely unnecessary. For healthcare, see Two- or multi- step approach and Organ inventory.
-As a way to accurately represent gender diverse people, "other" is, quite literally, othering. "Prefer not to say" or similar wording can be an excellent option, but not if it is the only option outside the traditional gender binary that is available to users - in that case, it becomes no longer a preference but a requirement not to say.
--- -"Researchers have also developed methods to respond to challenges involved in data collection about sexual and gender identities which are culturally specific and unique. For example, in 2011, the government of Nepal attempted the world’s first census in which respondents had the option of choosing 'Male,' 'Female,' or 'Third Gender.' The effort was not successful for a number of reasons, among which was that large proportions of the gender minority population did not identify with the term 'third gender.' Subsequent research determined that the use of culturally specific terms such as 'Methi' and 'Kothi' would have increased the effectiveness of the census effort."
-
While these can be appealing for the choice they afford users, they come with notable use and implementation considerations.
-This approach provides much of the benefits of an expansive list, and removes the drawbacks discussed above. However, it comes at the cost of making data storage and analysis more expensive, challenging, and time-consuming. It may additionally still contribute to user confusion for anyone who is uncertain where or how the information will be used or whether there are expected responses.
-For smaller projects, however, this is still an option. Or you can offer this to a small subset of users who find that the available options don't fit - see Not listed here.
-Especially when providing expansive lists or allowing write in fields, ensure that terms that are not mutually exclusive must be chosen as though they are. This is the reasoning behind guidance for a two-step approach, described in more detail in Two- or multi- step approach, which "measures assigned sex at birth and self-reported gender identity at the time of the survey" (Park, 2016)
---"...some terms aren’t mutually exclusive, and framing them as such is offensive, Mons said: 'For example, if we list Man, Woman, Non-Binary, Trans Man and Trans Woman, does that separation imply that someone who is trans and identifies as a man is not a man?'"
-
(Gossett, 2020) emphasis mine
-In addition to reinforcing binarism, this approaches tends to reinforce sexism as well, and can further reinforce other prejudices as they intersect with gender.
--- -"Moreover, these works tend to codify (literally, to write into code) essentialist, stereotypical characterizations of male and female communication patterns and present them as universal, context-free, scientific truths."
-
For transgender individuals, automatic categorization as a gender they are not is often a frequent and painful occurrence. We should take care not to introduce unnecessary causes of harm into our systems, especially when they are less accurate and helpful than user self-identification.
-Gender isn’t one-to-one with pronouns (or terms of address). If you want to know how to refer to someone, ask pronouns separately.
-Including "decline to specify" or a similar opt-out response is always a positive addition unless the information is actually necessary. However, as mentioned above, don't use this as a replacement for the inclusion of terms that actually match the identity of the individual. Opting out should be a choice, not forced due to lack of other options.
-Including "gender not listed here" or a similar response provides an out for anyone who does not feel represented by the available options. You could follow this up with a free text entry field.
-Avoid treating gender as an immutable category - make sure users have the ability to edit it.
-As mentioned briefly above, a two-step approach separates gender and assigned sex at birth, allowing healthcare systems additional information about the patient. See WPATH guidelines on EMR and associated reference for more details. -This may also be expanded to a multi-step approach, with questions covering some/all of
-…but only if the data is necessary.
--- -Provide a means to maintain an inventory of a patient's medical transition history and current anatomy. An anatomical inventory would allow providers to record into the chart (and/or update as needed) the organs each individual patient has at any given point in time; this inventory would then drive any individualized auto-population of history and physical exam templates. This inventory should be uncoupled from the patient's recorded gender identity, assigned sex, or preferred pronouns.
-...
-These procedures, however, also should also be un-coupled from any gender-coded template so that an individual coded as male who has had a hysterectomy, for example, could have that history documented. In addition, sex-specific organ procedures and diagnoses relating to these organs should be un-coupled, so that (as an example) a prostatic ultrasound may be ordered on a patient registered as female, or a cervical pap smear ordered on a patient registered as male. Such practices would allow enhanced decision support for transgender-specific care, such as medication interactions, organ- and sex-specific preventive health alerts, or accommodations for sex-specific laboratory normal value ranges.
-
Refer to the reference linked above for detailed examples of organ or surgical history inventories.
-Additional reference (added Sep 2024): (Bourns, 2023)
-Avoid questions that contrast ‘male’ and ‘trans male’ or any parallel set of terms. Either contrast ‘cis male’ and ‘trans male,' or allow respondents to choose multiple responses.
-These systems add additional requirements and restrictions. In cases like these, it may be helpful to have an additional field for legal gender/sex designation (usually set to F, M, or X) in order to allow for alignment with existing documentation without preventing self-identification on the part of the individual.
-Healthcare systems in particular may benefit from Organ inventory and Two- or multi- step approach.
--- -Preferred name, gender identity, and pronoun preference, as identified by patients, should be included as demographic variables (such as with ethnicity). These would be captured in readily amendable, optional fields that are separate from the patient’s state-listed name and sex or gender designation, which may continue to be used for billing purposes in circumstances when the patient has not yet obtained legal change of name and/or sex or gender designation. Note that some patients may identify as ‘genderqueer’ and prefer the use of neither pronoun. While lists of current common gender identities, sex options, and pronoun options are provided [in original document, see source], ideally field parameters would be easily amended to reflect changing paradigms and social trends within transgender communities.
-
-- -"In 2014, Facebook expanded their gender options from 2 to 58 for English speakers in the US and UK. The gender options they added were created in consultation with the LGBTQIA community and range from 'gender non-conforming' to 'two-spirit' to 'trans female'. The corporation later added the ability to identify as more than one gender and to input a custom gender. ... While these changes may appear to be progressive, Facebook’s databases still resolve custom and non-binary genders to Male and Female on the backend based on the binary gender that users select at sign-up where the custom option is not available. Here is how the Facebook Marketing API views gender: 1 = Male, 2 = Female. So while a user and her friends may see her presented as the gender she elects, she is a 1 = Male or 2 = Female to any advertisers looking to purchase her attention."
-
Uses a list of genders developed by the Open Demographics project. (McCabe and Beach, 2019) (Stevens)
-Includes Male, Female, Other, and Decline to Specify as options for a gender field, and the field can be null. (Salesforce)
-Includes Male, Female, Not Specified, and Non-specific. Defaults to not specifed. (Adobe) (Adobe)
-Uses three different fields: sex assigned at birth, legal sex and gender identity. (Landman, 2017)
- -
-
-
- Headers and text in blockquotes are handwritten; plain text is image description.
-There are two dots spaced a bit apart, labeled 'Male' and 'Female'
-The same two dots from the previous diagram, now with a horizontal line connecting them.
---Let's think about expanding this in two ways...
-
Following this, two arrows point downward to two columns titled Models 3a and 3b, separated by a squiggle.
-The connected dots from Model 2. Now, a vertical axis rises midway between male and female, and its extreme is labeled 'agender.' Arrows helpfully indicate the directions of 'less gender' and 'more gender.' There is a dotted line between agender and male, suggesting a triangle shape.
-The connected dots from Model 2, but instead of the horizontal line ending at the points marked male and female, it continues off into seeming infinity.
---These are just two points, not "extremes"
-
A grayscale circle simulating a color wheel, with a slice scaled up to examine. "Type" of gender is labeled as varying as the color wheel is circled, while "amount" of gendered is labeled as varying between the center and the edge of the circle.
---If we wanted to "place" male and female here, they're probably regions, not discrete points.
-Just as "sky blue" and "navy" are both blue, we have umbrella terms that contain or overlap other terms"
-
In correspondence with this text, the color wheel image has two areas circled in dotted lines, labeled 'male?' and 'female?'
- -
-
-
- To order this as a shirt, please fill out the order form. (Sep 2024: no longer taking orders via this method)
3" x 8"
-default: blue
-patch, print, greeting card, sticker, shirt, pin
- -
-
-
- This is a collation of responses to a fediverse post I made.
-For reference, I also have a list set up at /give that lists a number of great nonprofits.
-If you are listed here and would like to be removed, just get in touch with me.
-Thank you to these wonderful folks (and many more who chose to remain uncredited) for the links/orgs:
- - -
-
-
- 40-something inches long, brass hardware, custom dyed.
- -
-
-
- A green memo pad. The text block is held on by a piece of waxed thread around the spine, which allows it to be replaced if desired.
-
-
-
- Original print greeting cards.
- -
-
-
- Original print greeting cards.
- -guestbook entries are manually added and moderated :)
-yes, you may sign again even if you've been here before - just no spamming please!
-if the embed below is not working for you, you can open the guestbook form in a new tab or just contact me any way you want with your desired message
- -J'myle Koretz on :
---It was delightful to meet you at the Vashon art walk a couple weekends back. Kestrel was awesome, and your [Brooke's] jackets were so cool!
-
Jack on :
---All the things you make and do are so cool and I feel so lucky every time I get to see them (including the ones that now live in my home!)
-
hello on :
---thanks for being gay and weird pal
-
Fén (Spirits) on :
---Hi you make really good art and crafts and have been awesome to get to know ^^ Keep on keeping on~
-
Olive on :
---Hi! You're cool :))
-
Fern (Rainbow) on :
---I think you're awesome and this is really impressive 🥺
-
handmade ghost on :
---Everything Lee makes is a gift to the world--just knowing ze made something new brings me joy and sends me scrambling to this site!
-
jay on :
---EGG
-
pqqq on :
---Hello from Fedi! Your site is absolutely wonderful, and I love that you're adding a guestbook, too!!
-
Morgan on :
---You make good art and good takes on gender 💜
-
Lisa on :
---Ask not for whom the snoot is booped. It is booped for weevils.
-
nathanlovestrees on :
---honk!
-
✨pencilears✨ on :
---Hello! I'm glad we're still doing guest books on websites.
-
brhfl on :
---hey hi! just giving you some test data and also basking in the nostalgia of a guestbook :)
-
Eedlipherus C. Bigribs on :
---Signing the guestbook so you have data to format on the page lyao.
-And this is a second paragraph, because, honestly, this is more interesting than the QA I get paid to do, so I'll just make this bit long enough to wrap for a few lines, yep, maybe a little bit more, uh huh, that's right, eat your heart out, Lorem Ipsum.
-
jade on :
---Hi Lee!
-
alex tax1a on :
--- -Hi!! signing the guestbook, so you have at least one datum.
-
-
-
- Hand carved stamp of some tummy hair, in celebration of testosterone.
-4" x 6"
-default: black
-patch, print, greeting card??
- -
-
-
- Fiber from Paradise Fibers. 70% merino/30% nylon. Hand-dyed by me.
- -
-
-
- Fiber: Jacobs wool that I scoured and combed myself.
- -
-
-
- Recently, I got an iPad for art and immediately fell down the rabbit hole of Glitch and web development.
-When creating the rescue Trans rescue site, I started with a right-aligned navbar. While developing and testing on my iPad, I got in the habit of hovering my hand over the top-right corner of the tablet, always ready to try the light/dark toggle or switch pages.
-And then I thought about left-handed people - left handed tablet or touchscreen users in particular. Reaching across the screen repeatedly would start to be a real drag, wouldn't it?
-Ok, let's make a toggle!
-(Want to just see the outcome? Head on down the page to the summary.)
-First we'll need some HTML for our button. I added this to my menu:
-<button id="alignment"
- title="toggle left/right navbar alignment"
- aria-label="toggle left/right navbar alignment"
- class="menu-link">
- <!-- autopopulated by nav.js -->
-</button>
-It's important to note here that I added it to the beginning of the menu list. I want it to be the first item in the list so that it points over the left side of the screen with nothing obstructing it.
-Let's quickly take a moment to ensure that the toggle only shows up on wider screens with a bit of CSS:
-@media (max-width: 500px) {
- #alignment {
- display: none;
- }
-}
-Now users on phones won't have an unhelpful button taking screen space.
-Now let's move to nav.js and define some consts for ease of use. We're going to use Font Awesome icons for this button, so we'll go grab their HTML for the left and right pointing hands.
const ALIGN = "alignment"
-const LEFT = "left"
-const RIGHT = "right"
-
-const LEFT_ICON = '<i class="fa-regular fa-hand-point-left" aria-hidden="true"></i>';
-const RIGHT_ICON = '<i class="fa-regular fa-hand-point-right" aria-hidden="true"></i>';
-We'll use localStorage to store and retrieve alignment preferences:
let align = localStorage.getItem(ALIGN);
-and we'll grab the button we defined in HTML:
-let alignToggle = document.getElementById(ALIGN);
-Next, let's structure out some functions. We'll fill them in more as we figure out what we need.
-function setAlignRight() {
- // If we're aligned on the right, the toggle should point to the left
- alignToggle.innerHTML = LEFT_ICON;
-}
-
-function setAlignLeft() {
- // If we're aligned on the left, the toggle should point to the right
- alignToggle.innerHTML = RIGHT_ICON;
-}
-
-// This function changes the alignment to the given value
-// It also runs at startup to set alignment
-function changeAlign(align) {
- switch (align) {
- case LEFT:
- setAlignLeft();
- break;
- case null:
- // If nothing is set, default to right alignment
- align = RIGHT;
- case RIGHT:
- setAlignRight();
- break;
- }
- // Set localStorage for next time
- localStorage.setItem(ALIGN, align);
-}
-
-// Run at startup
-changeAlign();
-
-// This function handles the actual flip-flopping of the alignment value
-function toggleAlign() {
- if (align === LEFT) align = RIGHT;
- else align = LEFT;
-
- changeAlign(align);
-}
-
-// Attach the toggle function to the alignToggle as a click listener
-alignToggle.addEventListener("click", toggleAlign);
-So that gets us the basic functionality of changing the icon when the toggle is clicked. However, it does nothing for the navbar alignment. What do we need for that?
-Well, that depends on your navbar CSS. For this, let's run through the simplest possible version: your navbar is a flexbox and all items are treated equally. Maybe your CSS looks sorta like this:
-#navbar {
- position: sticky;
- top: 0 px;
- width: 100%;
- display: flex;
- /* This is the line that matters to us */
- /* we'll want to swap between flex-start and flex-end */
- justify-content: flex-end;
-}
-Let's add that justify-content setting to our JS (as well as a line to fetch the navbar by id):
let navbar = document.getElementById("navbar");
-
-function setAlignRight() {
- alignToggle.innerHTML = LEFT_ICON;
- navbar.style.justifyContent = "flex-end";
- // If you have other necessary style changes, add them here
-}
-
-function setAlignLeft() {
- alignToggle.innerHTML = RIGHT_ICON;
- navbar.style.justifyContent = "flex-start";
- // If you have other necessary style changes, add them here
-}
-Now, the menu should re-orient itself when we interact with the toggle. However, you'll notice that the left-aligned menu shows up in the same order as the right aligned menu: handedness toggle first, then the rest of the menu.
-(Forgive the lack of continuity with the header image.)
-
I don't want that; I want the handedness toggle to always point, unobstructed, to the side of the screen it moves things to. So let's move it around when we set alignment. It'll need to be the first item in the menu list for right-handed alignment, and the last item for left-handed. We can do that with prepend() and append().
function setAlignRight() {
- alignToggle.innerHTML = LEFT_ICON;
- navbar.style.justifyContent = "flex-end";
- navbar.prepend(alignToggle);
-}
-
-function setAlignLeft() {
- alignToggle.innerHTML = RIGHT_ICON;
- navbar.style.justifyContent = "flex-start";
- navbar.append(alignToggle);
-}
-Cool! Now we have a menu that re-aligns itself and repositions the alignment button.
-
Oooh, but wait: keyboard navigation is broken.
-When I tab over to the alignment button and hit Enter/space, it does what we expect, but it also loses keyboard focus. Because of that little prepend()/append() move, the element is removed from the DOM and re-added in a new location - now without focus. We'll need to add focus back to the alignToggle manually, so it's not lost.
We can do that with the .focus() function:
function toggleAlign() {
- if (align === LEFT) align = RIGHT;
- else align = LEFT;
-
- changeAlign(align);
-
- // Replace focus on the toggle that's been moved
- alignToggle.focus();
-}
-Ok, now focus is maintained... but it also shows up after a mouse click, not just a keyboard interaction. That's a little irritating. How do we fix that?
-toggleAlign() is an event handler, which means it can optionally take an event var. For "click" events, that event var includes a field detail which provides the click count. This can be used to disambiguate single vs double clicks, or it can be used to test for keyboard interaction, which creates zero clicks.
Let's add that in:
-function toggleAlign(event) {
- if (align === LEFT) align = RIGHT;
- else align = LEFT;
-
- changeAlign(align);
-
- // Zero clicks means this was a keyboard interaction
- // Replace focus on the toggle that's been moved
- if (event.detail === 0) alignToggle.focus();
-}
-Now we should only replace visible focus for keyboard interactions.
-
Want to see further changes? Found a bug with this implementation? Contact me!
-Here's the referenced HTML, CSS, and JS in full:
-<button id="alignment"
- title="toggle left/right navbar alignment"
- aria-label="toggle left/right navbar alignment"
- class="menu-link">
- <!-- autopopulated by nav.js -->
-</button>
-#navbar {
- position: sticky;
- top: 0 px;
- width: 100%;
- display: flex;
- /* nav.js handles justify-content instead */
- /* justify-content: flex-end; */
-}
-
-/* Remove the handedness toggle on narrow screens */
-@media (max-width: 500px) {
- #alignment {
- display: none;
- }
-}
-const ALIGN = "alignment"
-const LEFT = "left"
-const RIGHT = "right"
-
-const LEFT_ICON = '<i class="fa-regular fa-hand-point-left" aria-hidden="true"></i>';
-const RIGHT_ICON = '<i class="fa-regular fa-hand-point-right" aria-hidden="true"></i>';
-
-let align = localStorage.getItem(ALIGN);
-let alignToggle = document.getElementById(ALIGN);
-
-function setAlignRight() {
- alignToggle.innerHTML = LEFT_ICON;
- navbar.style.justifyContent = "flex-end";
- navbar.prepend(alignToggle);
-}
-
-function setAlignLeft() {
- alignToggle.innerHTML = RIGHT_ICON;
- navbar.style.justifyContent = "flex-start";
- navbar.append(alignToggle);
-}
-
-function changeAlign(align) {
- switch (align) {
- case LEFT:
- setAlignLeft();
- break;
- case null:
- align = RIGHT;
- case RIGHT:
- setAlignRight();
- break;
- }
- localStorage.setItem(ALIGN, align);
-}
-
-changeAlign();
-
-function toggleAlign(event) {
- if (align === LEFT) align = RIGHT;
- else align = LEFT;
-
- changeAlign(align);
-
- if (event.detail === 0) alignToggle.focus();
-}
-
-alignToggle.addEventListener("click", toggleAlign);
-
-
-
-
- 4 skeins of handspun yarn.
-
-
-
- 3" x 5"
-default: black and a random accent color
-patch, print, greeting card
- -
-
-
- Hand-lettered digitally. Happy Solstice to you and yours.
- -
-
-
- Happy Solstice to you and yours.
- -
-
-
- Hand carved stamp of a great blue heron. Based on a photo from birdpixel.
-4" x 8"
-default: black
-patch, print, greeting card, shirt
- -
-
-
- Rufous hummingbirds are remarkably aggressive.
-6" x 6"
-default: black and orange
-patch, print, greeting card, shirt
- -
-
-
- Fiber: Icelandic Lamb from Skagit Woolen Works
- -
-
-
- (or browse by tags)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- learning figma! isn't it fun!
- -
-
-
- A hand carved set of 4 stamps make up this iris print.
-default: varies. green stem and yellow leafy bits are standard, and iris petals are usually in a pink, purple, or orange with dark blue or purple detailing. other colors on request.
-print, greeting card
- -
-
-
- Hand carved stamp based on a photo of a dark-eyed junco.
-about 4" square
-default: black, gray, and sepia
-patch, print, greeting card
- -
-
-
- this pattern is offered at no cost, but I highly encourage giving to Palestinian relief work.
- -| abbr | -term | -
|---|---|
| MC | -main color (light in swatch shown) | -
| CC | -contrast color (dark in swatch shown) | -
| RS | -right side | -
| WS | -wrong side | -
| k | -knit | -
| sl | -slip | -
| wyif | -with yarn in front | -
| p | -purl | -
in this pattern, you will be creating floats on the front of the work, then picking them up and knitting into them 3 rows later. Do this as part of the stitch you are working, not instead of! You should have the same number of stitches afterwards.
-when you pick up a float, you want it to sit in front of the worked stitch, and untwisted. To achieve this, I have been catching the float on my right needle by dipping the needle down and under the float, from the front to the back. From there, I work the stitch, then slip the float over the new stitch on my right needle.
-knit flat, the pattern repeat uses 6n + 1 stitches. In the swatch shown, I cast on 29 stitches - 2 on each side for selvedge, then 25 -- 6 * 4 + 1. To simplify things, I'll write the pattern for the swatch, selvedge and all - but please adapt as you see fit.
-with MC, cast on 29 stitches.
-end after rows 5 or 9 of the pattern repeat. replace row 6 or 10 with the following:
-to end my swatch, I did the following:
-with MC, cast on a multiple of 6 stitches. knit in plain stockinette until you are ready to start the pattern.
-end after rows 5 or 9 of the pattern repeat. replace row 6 or 10 with the following:
-with MC, knit in plain stockinette until you want to bind off, then bind off.
- -
-
-
-
-
-
-
- Hand carved stamp based on a photo of a killdeer.
-1.5" x 2"
-default: black
-patch, print, greeting card
- -
-
-
- a reduction print. total print run of 33.
- -
-
-
- Apparently I knit shrimp now. Not for sale, only a gift. Optionally stuffed with catnip in addition to stuffing.
-about hand-sized. depends on the yarn I use
-default: pinkish
- -
-
-
- Also available in maple and serrated-edge leaves
- -
-
-
- Fully designed and created by me. Talk to me about designing your harness next!
- -
-
-
- Hand-stitched leather keychains made from scrap leather.
-1-4"
-
-
-
- wet molded and hand-stitched
- -
-
-
- Variety of leather-bound journals. See the shop listing for paper and size details.
- -
-
-
- The pages are sewn onto leather straps which are then woven into the cover.
-4" x 6" ?
-lined 60 lb
- -
-
-
- If I have linked a tool, I really like that specific tool and find it stands out. Otherwise, idk, I've used precisely one edge beveler, I have no idea how it compares.
-
-
-
- Fiber: Jacobs (light grey) from Skagit Woolen Works
- -
-
-
- A hardcover book with lined pages.
-~ 4" x 6"
-
-
-
- he's just a lil guy
- -
-
-
- From a pattern by Buckleguy.
- -
-
-
- Full grain leather, handstitched. Great fidget.
- -
-
-
-
-
-
-
- a reduction print. total print run of 8 test prints and 18 final prints.
- -
-
-
- Hand carved stamp based on a photo of lupines.
-6" x 5.5"
-default: green leaves, blue/purple/pink blossoms
-print, greeting card, patch, shirt
- -
-
-
- i have a lil maker's mark stamp now!
- -
-
-
- Designed for my mum.
- -
-
-
- bulldog harness with matte nickel fittings
-yeah i know this is in the leather tag and it isn't really leather. get at me.
- -
-
-
- Filled with catnip and polyfill.
- -
-
-
- today I decided to finally clean up the assets/img directory for this site. Since 2022, when I started this project, I've just been adding images directly to that directory with no further segmentation - messy of me, I know! It's gotten unwieldy and I'm starting to get worried about generic names leading to duplicates at some point, particularly for the non-gallery images where I have a tendency to use lots of mushroom images.
so it's time to move them into year-based folders. Let's talk about how I did that. bash away!
(want to skip right to the completed script?)
-findlet's start with the basics: a list of posts. find gets us everything under a specific directory - in this case, the _posts directory. We can filter out the directories a few different ways, but I piped the find output through a basic grep looking for .md in the filename.
for FILE in $(find _posts | grep .md)
-do
- # TBD
-done
-grepgrep can also help us get image names with the regex "name:.+jpg|png". I add name: to the regex because there are very occasionally images that aren't the featured image for the post, and those don't fit the pattern of name: <img>. Since there's so few of those, I ended up handling them manually.
to make grep work with regex, it needs the -E flag.
# gives us
-# name: <img>
-# note the 4 spaces at the beginning of the line
-IMAGE_LINE=$(cat $FILE | grep -E "name:.+jpg|png$")
-cutthat output gets us the full line of text that includes the image filename. Let's trim out what we actually want.
-below, -d sets a delimiter, and -f chooses what field we want to return. Because there's 4 spaces before name, our field index is actually pretty high - cut is creating 4 empty strings.
IMAGE=$(echo $IMAGE_LINE | cut -d ' ' -f 6 -)
-or, for brevity:
-IMAGE=$(cat $FILE | grep -E "name:.+jpg|png$" | cut -d ' ' -f 6 -)
-with cut, we can also get the year of the post:
YEAR=$(echo $FILE | cut -d '/' -f 2 -)
-sedthere's two major things we need to do with the information we've gathered:
-we can do replacement with sed, where our pattern should be something like this: s/$IMAGE/$YEAR\/&\ (the & subs in the found string - in this case $IMAGE). We could also use comma separators if we don't want to escape the slash, like s,$IMAGE,$YEAR/&, - I did this for ease of reading.
by default, sed prints to standard output, so we'll tell it to edit in-place instead with -i. Here's our full sed command:
sed "s,$IMAGE,$YEAR/&," -i $FILE
-mving and shaking(my mom thinks I'm funny.)
-now we'll handle moving the image file from its original location into a new directory. let's create our image paths, source and destination:
-IMG_DIR=assets/img
-NEW_IMAGE=$IMG_DIR/$YEAR/$IMAGE
-IMAGE=$IMG_DIR/$IMAGE
-trying to mv the images will immediately cause problems, because the year directories don't exist yet. A simple check gets us past that:
if [ ! -d $IMG_DIR/$YEAR ]
-then
- mkdir $IMG_DIR/$YEAR
-fi
-finally, we can mv the image:
mv $IMAGE $NEW_IMAGE
-here's our final script:
-for FILE in $(find _posts | grep .md)
-do
- # parse image and year info
- IMAGE=$(cat $FILE | grep -E "name:.+jpg|png$" | cut -d ' ' -f 6 -)
- YEAR=$(echo $FILE | cut -d '/' -f 2 -)
-
- # replace in-place in file
- sed "s,$IMAGE,$YEAR/&," -i $FILE
-
- # path creation
- IMG_DIR=assets/img
- NEW_IMAGE=$IMG_DIR/$YEAR/$IMAGE
- IMAGE=$IMG_DIR/$IMAGE
-
- # create dir for year if it doesn't exist
- if [ ! -d $IMG_DIR/$YEAR ]
- then
- mkdir $IMG_DIR/$YEAR
- fi
-
- # move image
- mv $IMAGE $NEW_IMAGE
-done
-questions? errors? ping me!
- -
-
-
- do you have a favorite git flag? I do. let's talk about git add -p.
okay, it totally doesn't. it's short for --patch. but 'partial' is my mental trick because it makes a little more sense to me, and it might help you remember it too.
also, TIL: this command is a shortcut to the patch subcommand of the --interactive flag - a flag I have never in my life used. wild the things you learn when you read the documentation.
a classic scenario: you've realized your uncommitted work doesn't just encapsulate one change, and it's not so easily split as to be separable per-file. git add -p instead lets you choose to stage changes chunk-by-chunk, or, as git terms it, hunk-by-hunk.
...are not perfect. you can split hunks, but only so much, and there's going to be cases where your changes are so mixed in as to be inseparable.
-I use -p almost every time I use git add, not because I know I will need to split up my work, but to review what changes I made.
the output from git add -p highlights extraneous trailing spaces!
every hunk -p shows you comes with the following single-character choices:
yes and no respectively. either add the hunk, or don't.
-quit. exit the whole -p process.
I rarely use these two. that said, I try to remember these two as all and done. stage all of the hunks in the current file, or be done with the current file.
-only shows up sometimes, because it lets you split the current hunk. if unsplittable, you won't see this option.
-edit. read the documentation for manually editing if curious because I don't use this and can't advise on it.
- -
-
-
- These are collated USA-based resources I have found while seeking out trans surgical care networks.
-
-
-
- my first wallet design. made of a variety of discount leather with various imperfections. fully hand-stitched.
- -
-
-
- This piece is unfinished and is presented in only partial form.
-5 or so years ago, I’m in a Facebook group. Rather than my usual lefty fare, it was more all-over-the-map; it was focused on socializing, support, and advice for younger employees at my current employer.
-Every once and a while, discussion would turn to something like:
---I’m facing sexism at work…
-
or any relevant comment about any -ism you can imagine. Unfortunately, there was no shortage of reply guys ready to diminish, second guess, disbelieve, and more. And as a “nonpartisan” space, it of course had a “no politics” rule!
-You can probably see what’s coming a mile away.
---the total complex of relations between people living in society
-
is how Merriam-Webster defines politics. It is not the only definition, and it is not the first definition, but it’s nicely non-circular and doesn’t get us bogged down in what is and isn’t government policy.
-That might be a more expansive definition than what you’re used to. Let’s take a detour.
-The idea that “the personal is political” has been around since second-wave feminism, 1960s at least. For example, here’s the Combahee River Collective Statement referencing it in 1977, having already worked to further and expound on it:
---A political contribution which we feel we have already made is the expansion of the feminist principle that the personal is political.
-
— Combahee River Collective Statement
-“The personal is political” and associated political movement is pushback against an insistence on individualizing systemic issues. Crenshaw frames it as a “process of recognizing as social and systemic what was formerly perceived as isolated and individual” (Mapping the Margins: Intersectionality, Identity Politics, and Violence against Women of Color by Kimberlé Crenshaw) and it’s vital in our current understanding of things like sexual assault and domestic violence as — forgive me for repeating the word yet again — systemic issues.
-But why might people institute a “no politics” rule?
-A frequent argument that gets made is that “we just don’t have the resources to moderate that.” Regardless of whether it’s right, this is what’s effective and what we have the bandwidth for.
-As a frequent moderator, I see the appeal in this. Political discussions can be tense, with high emotions and hard stances. After all, the personal is political - someone may not be speaking of some theoretical risk but to a concrete fear that hangs over their head daily. Members will get invested, and feelings will be hurt.
-Especially in environments where you want to welcome a large and diverse group of people, the aim should be to make the membership and prospective membership comfortable (possibly while also warding off trolls who want to argue).
-For some, "the law isn't about shaping society, it's about who gets punished" (I Hate Mondays by Innuendo Studios). Politics are a method of assigning blame. "No politics" means no one is to blame.
-6 or so months ago, I'm in a Discord server. Rule 1 states that no politics or religion are allowed. Interestingly, it also states that "insults, racism, sexism, homophobia, transphobia, and all other discriminatory speech" are not permitted.
-sections
-notes
-
-
-
- Happy pride!
-3" x 2.5"
-default: yellow, purple, and black
-patch, print, greeting card
- -
-
-
- Idea by Cassandra.
- -
-
-
- Made a few variations of this; check out the shop for more details and options:
- - -
-
-
- Created in cooperation with Julia Pinedo.
-Adapted from a workplace session on pronouns and gender identity.
-and for that matter, what's gender?
---Gender is derived from the Latin work genus, meaning “kind” or “type.” Gender is the social organization of bodies into different categories of people. In the contemporary United States, this sorting into categories is based on sex, but historically and cross-culturally there have been many different social systems of organizing people into genders.
-
– Transgender History: the Roots of Today’s Revolution, Susan Stryker (she/her)
---Gender literally means “kind” or “type” or “classification” – it’s related to words like genre and general – and has no necessary connection to real-world biology.
-
– What’s Your Pronoun: Beyond He & She, Dennis Baron (he/him)
-this is all new, right? ...right?
---...Francis Brewster coined E, es, and em in 1841, and Charles Crozat Converse announced thon and thons in 1884...
-
--...ze appears in 1864, introduced by someone known only by the initials J.W.L., and hir first popped up a century ago, invented, or at least introduced to readers in California, by the editor of the Sacramento Bee on August 14, 1920.
-
– What’s Your Pronoun: Beyond He & She, Dennis Baron (he/him)
---In 1916, when Jeanette Rankin, of Montana, became the first woman elected to the US House of Representatives, the Minneapolis Star Tribune ran this headline challenging the grammatical rule that says the masculine pronoun [he] can refer to women: “Can ‘She’ Be ‘He’, a Congressman, and Be Woman?” Pronouns aren’t just a part of speech. Pronouns are political.
-
– What’s Your Pronoun: Beyond He & She, Dennis Baron (he/him)
---In 1841, Francis Brewster took time from his medical practice to write a grammar in which he labeled his new pronouns “masculor feminine.” Masculofeminina is Latin for “man-woman, or hermaphrodite*,” and Brewster’s use of this unusual term is the first hint at nonbinary gender in a grammar book.
-
– What’s Your Pronoun: Beyond He & She, Dennis Baron (he/him)
---Men seemed eager to blame women for the pronoun problem. If finding a new pronoun was the job of the women’s movement, then singular they was the fault of the women’s movement.
-
– What’s Your Pronoun: Beyond He & She, Dennis Baron (he/him)
---Calling words “invented” makes them sound artificial. But word coining is a natural process, one that’s essential for any language to survive.
-
– What’s Your Pronoun: Beyond He & She, Dennis Baron (he/him)
---…the use of appropriate pronouns has a positive impact on mental health, and the use of inappropriate pronouns can form part of a general pattern of harassment and abuse.
-
– What’s Your Pronoun: Beyond He & She, Dennis Baron (he/him)
-...and how do I help?
---It is not unusual for speakers of English to find it difficult to switch pronouns about a person, because we (English-speakers) don’t have that many pronouns, historically, and we tend not to introduce new ones quite as often as we introduce, say, nouns.
-
– pronouns 101: introduction to your loved one’s new pronouns, Kirby Conrod (they/them)
-beyond just "they"
-While I can't speak for all neopronoun users, here are some of my personal reasons for using ze/hir in addition to he/him:
-not currently considered polite by some; do not use it for others unless they use it for themselves
-not currently considered polite by some; do not use it for others unless they use it for themselves
- -
-
-
- Riso printed!
- -
-
-
- (originally posted on fedi)
-update: in gender census' 2025 report the numbers continue to be very similar, sitting at about 62% and 30% respectively.
-update: in 2024, ~60% of respondents use "nonbinary." ~29% use "enby."
-according to gender census, which surveys "people whose genders are not adequately described, expressed or encompassed by the restrictive gender binary":
-source: gender census - identity words (this was a "choose all that apply" question, not "either/or")
-we don't know the overlap - it could be that half of nonbinary people also use enby, or that bunches of people use enby who don't call themselves nonbinary
-either way, there's a big chunk of nonbinary folks who don't call themselves enbies - at least half of us
-if you're talking about the entire community or the gender category: please consider just saying nonbinary
- -
-
-
- idk 6" x 4" ish?
-good for fountain pens
- -
-
-
- A small highlighter-orange journal with blank fold-out pages and floral coloring pages, including flocked pages, from BLOOM by Alli Koch.
-
-
-
- Fiber: Merino/silk 70/30 from Jakira Farms in colorway Orion
- -#2e303e
-#3c3f52
-#ebeeef
-#dbe1e3
-#18737b
-#25b0bc
-#94195d
-#ee9fcb
- -
-
-
- inspired by this YouTube video
- -
-
-
- Fiber: Malabrigo Nube in colorway petrichor
- -
-
-
- piñatex is a leather alternative made from pineapple leaf waste!
- -
-
-
- My first pair of socks, knit with Seattle Sky Dyeworks Temporum yarn in colorway Salmon Run.
- -
-
-
- we got PINS yahoo!
-hard enamel, rubber backings
- -
-
-
- Knitted dice bags in pride flag patterns.
-Available flags at current:
-I also have several varieties of rainbow gradient yarn which will produce varying results.
-For any flag not on the list, please do still message - I'm happy to get the colors missing to do it! For example I would only need a gray for asexual, so it's easy to add :)
-usually sized to hold 1-3 dice sets, but I can aim for a specific size on request.
-Based on flag chosen
- -
-
-
- Speedball Model B printing press. Goes for ~$100 - I got it through Blick
-I've accomplished multi-layer prints in two ways thus far:
-
-
-
- Cataloguing paper I have worked with and any notes.
-Lightweight Rives Printmaking Paper, Cream
-
-
-
- Hand carved modular stamps for when you want a chatty skeleton to yell your pronouns.
-Available pronouns at current:
-If you use pronouns that aren't in this list, let me know and I will work on adding them.
-roughly 3" x 1.5"
-default: random
-patch
- -
-
-
- Designed for my dad.
- -
-
-
- an incomplete list of books by/for/about queer folks. check it out and add your favorites.
- -
-
-
- 2" x 4" ?
-default: black
-patch, print, greeting card, sticker, pin
- -
-
-
- Based on a beautiful photograph by Jessamyn
- -
-
-
- Custom dyed. Stainless steel hardware.
- -
-
-
- Fiber from Woolgatherings. 100% rambouillet. Hand-dyed!
- -
-
-
-
-
-
- cat tempest.md
-You do look, my son, in a moved sort,
-As if you were dismay'd: be cheerful, sir.
-Our revels now are ended. These our actors,
-As I foretold you, were all spirits and
-Are melted into air, into thin air:
-And, like the baseless fabric of this vision,
-The cloud-capp'd towers, the gorgeous palaces,
-The solemn temples, the great globe itself,
-Ye all which it inherit, shall dissolve
-And, like this insubstantial pageant faded,
-Leave not a rack behind. We are such stuff
-As dreams are made on, and our little life
-Is rounded with a sleep.
-grep takes a file or files as an argument.
grep "'d" tempest.md
-As if you were dismay'd: be cheerful, sir.
-The cloud-capp'd towers, the gorgeous palaces,
-If there's no files, or if - is passed as an argument, grep reads from standard input. < reads a file into standard input (this is shorthand for 0<, 0 being the file descriptor of stdin).
grep "'d" < tempest.md
-# or
-grep "'d" - < tempest.md
-As if you were dismay'd: be cheerful, sir.
-The cloud-capp'd towers, the gorgeous palaces,
-<(command) creates a file descriptor for the output of command.
grep "'d" - < <(cat tempest.md)
-As if you were dismay'd: be cheerful, sir.
-The cloud-capp'd towers, the gorgeous palaces,
-(In this case, you could substitute cat tempest.md | grep "'d" - but <(command) is handy for cases where commands don't take stdin, or need to take in multiple files (such as comm).
cat can also take stdin when given - or no arguments.
grep "'d" < <(cat < tempest.md)
-# or
-grep "'d" < <(cat - < tempest.md)
-As if you were dismay'd: be cheerful, sir.
-The cloud-capp'd towers, the gorgeous palaces,
-it's <s all the way down.
grep "'d" << tempest.md
-heredoc> oh are we interactive now
-heredoc> what am i 'doing' anyway
-heredoc> tempest.md
-what am i 'doing' anyway
-(<< starts an ephemeral document or heredoc. The first argument given is the delimiter, and is more commonly EOF (end of file).)
the reference page is for informational posts
-(or browse by tags)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- the reference page is for informational posts
-(or browse by tags)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- the reference page is for informational posts
-(or browse by tags)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Updated 13 Aug 2024
-The content previously found on this page is now hosted on a standalone site.
- -(Site down? Not loading? Check for issues with Glitch, our hosting provider.)
- -
- he/him or ze/hir • Vashon, WA 98070
-
- lee.cattarin@gmail.com
- • this very website • linkedin.com/in/lee-cattarin
-
- Design-oriented platforms/infrastructure engineer with a well-rounded - background in algorithms, UI/UX, observabilty, databases, and accessibility. - Consistent customer focus with strong communication skills and a passion for - sharing knowledge. -
- -– Present
-- Creates and maintains websites for clients based off their concepts and - ideas. -
- -- ...a pleasure to work with. Ze pays close attention to detail, and - truly cares about making his creations accessible, including aspects - that I didn’t even know to ask for. As a bonus ze is extremely quick - and responsive! --
I really appreciate your willingness/ability - to meet me where I was at as far as communication and such was. I - feel a lot more comfortable asking clarifying questions than I have - with others.-
- Having participated in a few tangible projects that Lee has run online - I have admired hir clean, effective, and attractive websites for a - while. When Lee announced ze was taking on more work, I jumped at the - chance to have someone else rebuild my old handcoded one. My new - website from Lee is fantastic! Super easy to customize and update on - my own, which is what I have wanted for a long time. Ze was easy to - work with, very communicative about details of the process, and well - aligned with what I wanted from my site. I would not hesitate to - recommend Lee to anyone, at any technical comfort level, who wants a - nice website. --
- ...the site dev of my dreams -- ze brought my vision from a - PowerPoint to a webpage with clarity and a focus on accessibility - that made me feel real hope for the future. I am grateful for his - thought partnership and ongoing site support! --
- ...an absolute delight to work with. His communication is efficient - and timely, and his considerations in design are impeccable. When I - asked him if he could do this project, he came in on a level that I - had not anticipated. His contributions to my event have been - immeasurable. It’s exciting to continue to work with him, as we are - constantly finding new ways to upgrade our interface with the public. - The possibilities that he brings to the table are astounding. --
Microsoft, Redmond, WA
-–
-- Worked on green- and brown-field projects with customers, solving emerging - problems in infrastructure, devOps, and LLM/human interaction. Wrote - reports on product feedback to relay to product teams and improved - documentation. -
- -Riverside Refuge Studio, Vashon, WA
-– Present
-- Maintains website, storefront, and inventory for a diverse set of artistic - goods. Communicates with a range of customers, both digitally and - in-person. -
- -Rensselaer Polytechnic Institute, Troy, NY
-–
-- Helped students debug and develop in Python and learn core programming - concepts. Created daily quiz material and graded exams. -
-Microsoft, Redmond, WA
-–
-- Developed a chatbot add-on for the Azure Android application. -
-– Present -
-Designs, develops, and maintains website for learning and skill-sharing - listings for the fediverse (Mastodon). Manages all data and incoming - listing requests.
-– Present -
-Designs, develops, and maintains website. Manages communication and - logistics for a ~40 person global community building project.
-– Present
-Designs, develops, maintains, and creates all content.
-–
-Created and ran a global event for zine artists on the fediverse - for two years. Coordinated website, physical material shipping and - logistics, payment, and marketing for a 40–50 person project.
-Created a digital art exhibit and sale to raise money for charity. - Coordinated ~30 artists to contribute physical and digital work as well as - a collaborative sticker sheet. Raised ~2.3k for Trans Rescue.
-
- Bachelor of Science — Computer Science
-
- Rensselaer Polytechnic Institute, Troy, NY
-
-
-
- first in a series, probably.
-5" x 7.5"
-default: black
-patch, print
- -
-
-
- Fiber from Circle R Ranch. 100% alpaca, from Ruby the alpaca.
- -
-
-
-
-
-
-
- recently, I've been working on a website for a project called spoonfairies. On the providers page, we list a series of names along with their pronouns, location, and services offered. Visually, it looks like this:
-
at first, all three pieces of information in the top row had no extra styling - it was just a line of text with the same color and size throughout. The location bit also didn't exist yet, so we're going to briefly ignore it. Screenreader testing (with NVDA, specifically) informed me that, when reading through a long list of providers, parentheses become very irritating. Imagine hearing the following:
---Lorem Ipsum left parentheses she slash her right parentheses web accessiblity webdev. Dolor Sit left parentheses they slash them right parentheses housecleaning. Amet Consectetur left parentheses he slash him right parentheses webdev spreadsheets software.
-
...ad nauseam. Kinda irritating.
-put the pronouns in a span that provides special styling, and use ::before and ::after to apply parentheses.
<a href="/providers/lorem-ipsum">
- Lorem Ipsum
- <span class="pronouns">she/her</span>
-</a>
-.pronouns::before {
- content: "(" / "";
-}
-.pronouns::after {
- content: ")" / "";
-}
-the slash is the magic there. The string before the slash indicates the visual content, and the string after the slash is the alternative text content. I went happily on my way.
-plus, this is neat - now I can style the pronouns separately. Let's make them the standard text color rather than the link color, and a bit smaller, and a smidge opaque... nice.
-ooh, time to implement locations! I did my same ol' trick.
-<a href="/providers/lorem-ipsum">
- Lorem Ipsum
- <span class="pronouns">she/her</span>
- <span class="location">Tacoma</span>
-</a>
-.location::before {
- content: "📍" / "is based out of";
-}
-I even added actual alternative text rather than an empty string to provide some context. Pronouns, I figured, could exist without much context, as it's pretty common for them to follow directly after names in introductions, but location isn't as much of a given.
-again, style em up nice, more of a standard text look, right-aligned. Cool.
-...then I did some screen reader testing. Which I should have done directly after the pronouns bit. Turns out, I wasn't thrilled with what the <span>s did.
at least with fairly default settings in NVDA, the <span>s broke up the way the link was read out. Suddenly, I was getting:
--visited link Lorem Ipsum visited link she slash her visited link Tacoma
-
this is all one link, mind you. The <a> tag isn't broken into three links. But the <span>s apparently break up the screen reader output anyway (in NVDA, that's a continual caveat).
ooookay... what next?
-I moved away from my content approach entirely (well, I kept it around as a failsafe, but it's not running the show now). Instead, I switched over to an aria-label for the whole link.
<a href="/providers/lorem-ipsum"
- aria-label="Lorem Ipsum she/her is based out of Tacoma">
- Lorem Ipsum
- <span class="pronouns">she/her</span>
- <span class="location">Tacoma</span>
-</a>
-(technically, all this is templated to hell and back. I would hope that's obvious given I'm talking about lists of these entries.)
-now, after more screen reader testing, it reads out smoothly. The aria-label precludes the actual link text and cleanly says what needs to be said, with nothing breaking up the text and the whole thing easily recognized as one link. And I've got my fancy styling. Sweet.
-
-
- Hand carved stamp of seedlings to celebrate new life.
-1.5" x 4"
-default: sepia dirt, 2-tone green seedlings
-patch, print, greeting card
- -
-
-
-
-
-
- hand-printed designs on bella & canvas shirts. To order, fill out this form (Sep 2024: no longer taking orders via this method). Or just message me.
See the shirt tag to view available designs.
--- -Note: Shirt orders are done in rounds so I can take advantage of free shipping. This means if there's no demand, your shirt may wait a while. If you'd really like to get around this, I'll just ask that you cover the additional shipping.
-
-
-
- Stuffed with catnip, of course.
- -
-
-
- a shrimp. eir name is Gomez.
-idk 3" x 2" ?
-default: orange
-patch, print, greeting card, sticker, pin
- -
-
-
- welcome to the siblinghood of the traveling greeting card. it now has a home at siblinghood.quest.
-originated on the fediverse - see the #siblinghoodOfTheTravelingGreetingCard hashtag - with original idea thanks to noctiluca@scholar.social
-send a greeting card around the world. maybe more than one. we'll see.
-from a group of participants, a random list will be formed. every person is given the address of the next person in the list (the last person receives the first person's address). first person picks a greeting card, signs it, and sends it to their designated recipient; that person receives, signs, sends on, etc.
-this continues on to the last person, who finally signs and sends back to the first, closing the loop and ending the adventure. the first person gets to keep the card they originally sent out.
-if we want to, this can be easily re-run - add folks who want to be added, drop folks who want to be dropped, scramble the list, and start again.
-accepting new applications for this first round until December 15th, 2025. After the 15th, you may still put in for future rounds, but be aware future rounds are not confirmed yet and will not be confirmed for at least 6 months most likely.
-your marching orders (recipient address, order in loop) will arrive by email no later than January 1st, 2026.
-any number, but in the case of signups reaching 26 or greater people, the cohort will be split. Why 26? I'd like this to not run significantly longer than a year, and I'm very loosely estimating that each person will add 2 weeks to the timeline.
-update: we closed signups at 32 participants and will be running two loops! Don't worry, that doesn't change what you as participants do.
-I will edit the list as needed to accommodate any mailing restrictions. I will also re-roll if I am placed first on the list.
-4x6 inches/10x15 centimeters minimum size recommended so there is space for everyone to sign. please nothing wildly vulgar, I guess?
-you are allowed to include stickers or similar flat items for future recipients if you so desire. Please ensure your package meets weight guidelines for flat mail (in the US, forever/international stamped mail must be under 1oz; outside of the US please look into local guidelines - if you're struggling to be sure let me know and I'll do my best to help).
-the only people who will see your address are:
-Sign in any way you find meaningful. Please don't hog the card space - remember that as many as ~2 dozen people will be signing it!
-IMPORTANT! Only put a name, nickname, doodle, fedi handle, etc. on the card that you are comfortable having displayed publicly. In any posts that I make, it will not be directly connected to you. However, as this likely has a small cohort, please be aware that how you sign could be tied back to your fedi account.
-This will require a fresh envelope and stamp(s) as needed for postage.
-this is, at the end of the day, pretty flexible, but what I would really, really like is:
-all guidelines (which you can always find here!) will be repeated over email so you have a local copy. They may additionally be ported over to a standalone site if we find we want somewhere not-on-this-website for pictures and travel deets (don't worry, no addresses. Nothing more specific than country or state).
-each participant will be sent the name and address of the next person in the loop. They will also be sent their place in the loop, numbered from 0.
-I'll be doing my level best to track the card's movement, so as it is sent around (assuming I get check-ins from y'all! please help me out here!) I will send out reminders to folks to either check their mail or make sure the card gets sent out again. Some folks have specifically asked for this support and I don't want to let them down, so more communication is always better.
-read all that? too long, didn't bother? either way.
-sign up for the siblinghood of the traveling greeting card
-(if airtable is for any reason inaccessible to you, please reach out directly)
- -
-
-
- Pattern: Sideways Canvas
-Yarn: De Rerum Natura Antigone
- -| URL | -Last modified | -
|---|---|
|
- |
-
- |
-
-
-
- from a discussion on being a nonbinary man
-5" x 8"
-default: black, or yellow-purple gradient
-patch, print, shirt
- -
-
-
- simple pouch for coins, notions, what have you
- -
-
-
- Commission piece for a Doberman.
- -
-
-
- Fiber from Paradise Fibers. 40% merino/20% alpaca/20% camel/20% mulberry silk. Scrumptiously soft.
- -
-
-
- --work in progress! To be updated as I continue spinning.
-
in picture, from top:
-notes on handspinning various plant fibers. Fibers were purchased as a sample pack from Hearthside Fibers. Unfortunately, I spun the first sample - flax - in 2023, so don't have thoughts on that left in my head. I also received a mystery sample along with the 12 named samples that may have been lotus - it's the smallest skein in the image. The lotus was also spun in 2023.
-I am estimating this via yards per gram of fiber. Other than the flax and lotus (can't promise anything there), I aimed for my average spin with all of these and spun them on the same tool (Wheeliam, an Ashford Kiwi 3). Per-fiber notes mention whether they spun thick or thin if notable.
-| fiber | -yards | -grams | -yds/g | -
|---|---|---|---|
| banana | -33 | -22 | -1.50 | -
| flax | -33 | -27 | -1.22 | -
| hemp | -41 | -26 | -1.58 | -
| lotus | -16 | -12 | -1.33 | -
| rose | -60 | -29 | -2.07 | -
| tencel | -31 | -29 | -1.07 | -
| ramie | -7.5 | -5 | -1.5 | -
written up in order of spinning.
-
-
-
- A spotted towhee mid-leap.
-6" x 3"
-default: black head/back/wings, orange sides, red eye, and blue shading.
-patch, print, greeting card, shirt
- -
-
-
- A book of watercolor paper with an open spine and coptic stitching in light blue.
-
-
-
- Color test for 3 new ink colors.
- -
-
-
- Includes coloring pages from Animorphia by Kerby Rosanes.
-idk 6" x 4" ish?
-drawing paper
- -
-
-
- do you, like me, have a stash of greeting cards and stickers you'll never use? Too many notebooks and pens?
-let's do a stationery exchange! You'll still have too much stationery, but some will be new to you.
-originated on the fediverse - see the #stationeryExchange hashtag.
-similar to a Secret Santa (but not holiday-oriented), each participant is assigned a recipient. Participants assemble an envelope or package (estimate around $20-25 in value (not incl. shipping), but you are highly encouraged to pull from your own supplies, thrift, or make things yourself before buying new) and send to their recipients.
-because of international shipping prices, participants will opt in to broad location categories that they are able to ship to (US, EU, and rest of world). You are encouraged to provide more detail about your shipping abilities in a free text field following the opt-in question - if you'd like to check where you are unable to send mail to, try postcrossing's postal monitor. I will do my absolute best to accommodate shipping restrictions and needs. If you are unable to be matched with a gifter and/or a giftee I will reach out.
-sign up form will close on December 15th, 2025. Participants will receive their giftee's details by January 1st, 2026.
-ship-by date of February 15th, 2026, giving participants 1 and a half months to prepare and send their package. Packages will be expected to arrive by March 15th, 2026. If any recipient hasn't received a package by that time, I will follow up with their designated gifter.
-reach out if you need flexibility regarding timeline!
-if you are struggling with shipping costs, please feel free to put together a flat envelope with things such as greeting cards and stickers rather than more 3-dimensional items.
-if you are shipping ink, please be aware that liquids often have specific shipping rules. Look up the relevant rules for your location.
-if you are in the US, I quite recommend Pirate Ship which can help you get discounted rates with USPS and other services. You must make an account, but there is no fee for doing so.
-in the event of a drop-out or other issue, I will be providing one or two backup gifts. Participants may also opt-in to being backups as well to take some load off me. If backups are needed, I will cover the first one, then round-robin a randomized list of those who have opted in to backups.
-sign up for the stationery exchange
-(if airtable is for any reason inaccessible to you, please reach out directly)
- -
-
-
- Hand carved set of stamps depicting a stellar's jay about to take off. Based on a photo from birdpixel.
-6" x 4"
-default: black, blue, and brown as pictured
-patch, print, greeting card, shirt
- -
-
-
- 1" wide collar with nickel fittings and a 2" o-ring. sturdy 8/9oz leather.
- -
-
-
- Clear and holographic stickers ordered through DieCutStickers. See the stickers tag to view available designs.
-details below provided from StickerYou FAQs.
-Q: What do you mean by "removable adhesive"? Are my stickers going to fall off?
-A: Removable adhesive means our stickers are able to peeled off and re-stuck immediately if you need to reposition your sticker. This isn't going to weaken the adhesion, as we guarantee our stickers stick strong, last long, and won't rip or tear when exposed to the elements. This also means our high quality stickers peel-off residue free, and won't damage your stuff if you do decide to remove them.
-Q: I want to put the stickers on my laptop, but worry about damaging it. Will it leave residue behind?
-A: Our clear stickers will stick strong for as long as you need them, but when you’re ready to remove, the stickers will come off clean, without leaving any messy residue behind!
-Q: Can I stick these stickers outside, will they last?
-A: Yes they will! Our stickers are water-resistant, and if applied correctly our stickers will stick strong and last long outside for at least a few years.
-Q: These custom stickers are permanent, will they damage a surface if I try to remove them?
-A: Depending on the surface or material they are applied to, it’s possible that the surface may be damaged. Most surfaces that have been painted may be damaged when trying to remove a permanent die-cut sticker. For example, surfaces such as laminates and painted walls. It may be possible to remove permanent holographic stickers from lacquered / bake-dried surfaces such as car paint, however we do not guarantee that the surface will not be damaged.
-Q: Are these holographic die-cut stickers water, dishwasher and freezer safe?
-A: These stickers are certainly freezer safe and water repellant. They will also withstand dishwasher use thanks to their permanent adhesive. They are not recommended for use on fabrics and other similar materials and as such should not be put through a washer/dryer.
- -
-
-
- A leather spine journal with a striped front cover and navy back cover. It includes both blank pages and fold-out coloring pages from Fantastic Cities by Steve McDonald.
-Adaped from an introduction to Markdown in order to test and display styling of basic components of the site.
-Since your title (defined in the front matter) is your heading level 1, you should never use another heading level 1 in your body.
-The number of pound signs determines the heading level.
-It's also important not to skip heading levels. Don't jump from a 2 to a 4 or similar.
-You can use up to level 6!
-It's just unnecessary.
-You'll notice that I am putting blank lines between headings and plain text. This is necessary, or they won't render correctly.
-It's also important to put a blank line in between each paragraph. See what happens without it: -This is supposed to be a new paragraph, but it isn't.
-We can, of course, create bold and italicized text, or inline monospace text.
We can also create links, like this link to the home page.
-Sometimes you want to insert a visual break in your text that isn't just a new paragraph. You can use three dashes to create a horizontal line:
-This text will be below the line.
-Unordered lists can be created with dashes or asterisks. With dashes:
-With asterisks:
-Ordered (numbered) lists can be created with (surprise!) numbers. You can write numbers as you would normally, or you can just write the number 1 over and over, like so:
-This allows you to insert more information into lists in the future without having to renumber every following item.
-Both unordered and ordered lists can be nested. Just tab the nested section inwards:
-You can mix unordered and ordered lists when you nest.
-You can always just use quotation marks, of course, but if you are quoting a larger chunk of text it can be nice to use a blockquote.
-You format a blockquote by starting the line with a caret:
---This is a quote, and it will render differently than a paragraph.
-
If you want a quote to have multiple separate paragraphs, and still contiguously display as one quote, make sure to put a caret on the empty line between the paragraphs.
---This is a multi-paragraph quote.
-Here's the second paragraph.
--
-- Blockquotes can also have lists
-- They still have the caret at the front
-
You can write single words in monospace, or create code blocks:
3 backticks surround code blocks
-
-Code blocks can have syntax highlighting:
-<h1>Hello, world</h1>
-Tables in Markdown are kind of annoying to format. You use the pipe (|) character as well as dashes.
| Header 1 | Header 2 |
-|---|---|
-| data 1a | data 1b |
-| data 2a | data 2b |
-| data 3a | data 3b |
-
-When I remove the monospace block, you can see how this formats:
-| Header 1 | -Header 2 | -
|---|---|
| data 1a | -data 1b | -
| data 2a | -data 2b | -
| data 3a | -data 3b | -
-
-
- Commission piece.
- -
-
-
- Photographed in the backyard.
-6" x 8"
-default: black and green with yellow accents
-patch, print, greeting card, shirt
- -
-
-
- A card wallet.
- -If you're not automatically redirected, head to the new URL for this tag.
- -If you're not automatically redirected, head to the new URL for this tag.
- -If you're not automatically redirected, head to the new URL for this tag.
- -If you're not automatically redirected, head to the new URL for this tag.
- -If you're not automatically redirected, head to the new URL for this tag.
- -If you're not automatically redirected, head to the new URL for this tag.
- -If you're not automatically redirected, head to the new URL for this tag.
- -If you're not automatically redirected, head to the new URL for this tag.
- -If you're not automatically redirected, head to the new URL for this tag.
- -If you're not automatically redirected, head to the new URL for this tag.
- -If you're not automatically redirected, head to the new URL for this tag.
- -If you're not automatically redirected, head to the new URL for this tag.
- -If you're not automatically redirected, head to the new URL for this tag.
- -If you're not automatically redirected, head to the new URL for this tag.
- -If you're not automatically redirected, head to the new URL for this tag.
- -If you're not automatically redirected, head to the new URL for this tag.
- -If you're not automatically redirected, head to the new URL for this tag.
- -If you're not automatically redirected, head to the new URL for this tag.
- -If you're not automatically redirected, head to the new URL for this tag.
- -If you're not automatically redirected, head to the new URL for this tag.
- -If you're not automatically redirected, head to the new URL for this tag.
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (15 posts)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (30 posts)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (32 posts)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (12 posts)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (11 posts)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (50 posts)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (2 posts)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (8 posts)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (58 posts)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (20 posts)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (15 posts)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (3 posts)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (8 posts)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (16 posts)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (4 posts)
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Tiny leather-wrapped books in various colorations.
-some tiny mushrooms for a friend
- -
-
-
- currently offering these free in the shop with the code FREESTAMP
- -
-
-
- Limited number available for free in my square shop with the code "FREEPATCH"
- -
-
-
- Hand carved stamp of a smiling skull celebrating trans rights. Available now as clear and holographic stickers!
-3" x 1.5"
-stickers: black on clear or holo -patches: black -prints/cards: black with blue and pink letters
-patch, print, greeting card, sticker, pin
- -
-
-
- 6" x 7"
-default: black with pink-blue gradient
-patch, print, shirt
- -
-
-
- A complement to trans rights skull.
-3" x 2"
-default: black
-patch, print, greeting card, sticker, pin
- -
-
-
- there's snaps on both sides.
- -
-
-
- a vertical bifold. full grain leather, hand-stitched.
- -
-
-
- simple vertical card wallet. full grain leather, hand-stitched.
- -
-
-
- vertical card wallet with a zippered pocket. full grain leather, hand-stitched.
- -
-
-
- 1.5" collar with heart shaped o-ring and shearling lining stitched in place. stainless and nickel fittings.
- -
-
-
-
-
-
-
- tiny knife sheath.
- -
-
-
- Iteration on the zipper bifold.
- -
-
-
- bifold with a zippered pocket. full grain leather, hand-stitched.
- -