RSSAmplifier

Sarah Gebauer · Apr 14, 2026

Creating Drop Cap picker

0
Sign in to vote or save

Sarah Gebauer

Red baseball hat on the ground with a footprint on it

Published on 2026-04-14 18:04

Drop caps are my favourite part of typography, hands down. It sucks that they are not more common in the digital realm or in contemporary books. So if I want to change that, I need to start with my website.

Note:* This article covers only the frontend part as adding a simple feature is never a simple change.

The starting point

My starting point for this project a snippet of CSS which I need to copy and paste into each post

@font-face {
	font-family: "DropCapsSeriesA";
	src: url("../../assets/DropCapsSeriesA.ttf") format('truetype');
}
.post p:nth-of-type(2):first-letter {
	font-size: 3rem;
	font-family: "DropCapsSeriesA", sans-serif;
	-webkit-initial-letter: 3;
	initial-letter: 3;
	margin-right: 0.25rem;
}

This is not ideal for several reasons. First is that it complicates things in case there's a change in in the HTML structure. Second, it requires quite a bit of effort to change the font. Third, it has to be changed in each post. And last, my hope is that the webkit prefix won't be necessary in the future.

Design

The component itself is composed of visible part and hideable part. The part which is visible is the button and name of selected font for the drop cap. The hideable part is the dialog with select element, preview and button.

Screenshot of several states of drop picker

Development

Note: Currently there are some design differences as I'm creating how to work on my side projects first in Penpot and later in code.

Note: This post won't contain the whole 100 or so lines of code as it'll change when couple more components are added and common patterns extracted into separate components.

Communication with the outside

Since this post is a follow up post let's pick up where adding React to the project ended. On the page with post editor, there has been added a div element with id="drop-cap-picker" and two data-* attributes:

data-font="" data-font-list="['DropCapsSeriesA', 'RustFont', 'CSSCapped']"

The reason for those two data attributes is simple, in this use case it makes the most sense. It doesn't make sense to call backend because that would require adding a whole new endpoint or more. The React component lives on a web page generated on the backend, not inside an SPA or Next.js. So between window, publish-subscribe bus and data-*, the last one made the most sense.

The way how the data gets extracted by the component is that the top-level element get a reference with useRef and inside the useEffect, which is set to run only after mounting, there's a code which processes it:

JSON.parse(
  topElementRef.current.parentNode.dataset["fontList"].replaceAll(
    "'",
    '"'
  )

The substitution of quotes is at the moment necessary as Toes templates don't support JSONs inside the HTML attribute. That I'll have to address in the future.

Similarly the communication with the page works through events:

const event = new CustomEvent("drop-cap-font-selected", {
  font
});
topElementRef.current.parentNode.dispatchEvent(event);

Dialog

So this is the first time I've attempted to use a popover after reading about it in Amber Weinberg's post. Last time I had to use something similar it was too recent and we opted to use Floating UI instead. And it's simpler to use than I've thought.

First we need a button with popovertarget attribute:

<button
  className={"sl-button open-popover"}
  popovertarget="drop-cap-popover"
>
  Select Drop Cap
</button>

and then the target:

<dialog id="drop-cap-popover" popover={"auto"} ref={popoverRef}>

One thing that has tripped me was the fact that React doesn't support standalone attributes like popover without a value.

Font preview

Last thing that this post will cover is the preview section. For the time being the preview is a section with a list of characters. In the future iterations internationalisation is planned. But for now I can live with it.

Setting styles is almost never a pleasant procedure, so I've opted to use CSS variables. In the styles font-family property is set to try to find a variable or fall back to sans-serif.

.alphabet {
    font-family: var(--drop-cap-picker-font-family), sans-serif;
}

In the JSX part, if font has any non-empty value, it is assigned to the variable:

<section
  className={"alphabet"}
  style={{"--drop-cap-picker-font-family": font}}
>
  A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
</section>

Tweaking build process

Vite also created a compiled CSS file but the file name was dynamic. So in build.rollupOptions.output I've added assetFileNames key-value.

build.rollupOptions {
	output: { dir: "../../post-edit", entryFileNames: "index.js", assetFileNames: "index[extname]" },
}

That's all for this post, in the future post, I plan to look at media picker which might use some parts of the drop cap picker component.

Read the original on sarahgebauer.com

Comments

Nothing yet. Say the first thing.

    Sign in to join the conversation.