shadcn.io is not affiliated with official shadcn/ui
Font Picker Dialog Block for Vue
A writing app's document page with its type style dialog shown open: a family from three system stacks, weight, size from 12 to 48 pixels, line height and letter spacing, a live specimen and the CSS it makes with a Copy button.
Installation
Overview
Let writers set the type of a page and see it as they go, with this type style dialog for Vue with Ark UI. It opens from a document in Glyphlane, a writing app: the header holds an outline Button, and the document is drawn under a soft scrim inside a hairline frame with a plus mark on each corner. The dialog picks among three system families, Sans, Serif and Mono, each shown as an "Aa" in its own face in a Radio Group, then a weight and a line height with Toggle Groups, a size from 12 to 48 pixels with a Slider, and a letter spacing with a Select. Beside the controls a specimen restyles as you change them, with the CSS they make and a Copy Button. Apply to document, the only Shine Button, restyles the title and text of the page behind it. For preview the dialog is drawn open in place when the page loads; the header button opens the real Dialog, with the same state. For editors, note apps, reading modes and any app that lets people choose their type.
FAQ
Why is the dialog already open, and what do I keep?
So you can see it without a click. On load the dialog is a copy drawn in place over the page: a native dialog element that isn't modal, so nothing is trapped, hidden or focused, and the page around the preview keeps working. The header's Type style button opens the real Dialog, modal, with its focus trap, Escape and focus return, and the copy stays closed until a reload. Both show the same TypeFields component and share the block's state, so what you set in the copy is there in the real one. In your app keep the Dialog and drop the copy: the "in-place" state, the scrim and the dialog element inside the frame.
Why only three families, and are any fonts loaded?
No font is loaded or downloaded. Each family is a stack of system fonts that already exist on the device: ui-sans-serif and system-ui, ui-serif and Georgia, ui-monospace and a monospace fallback. That keeps the dialog instant, private and accurate, because what you see is what the device will draw. The dialog says "Uses your system fonts" for the same reason. To offer web fonts, add entries to FAMILIES with their font-family stacks, load them with your own @font-face or font service, and wait for them before applying.
What is the CSS line, and what does Copy put on the clipboard?
The specimen's style written as a font shorthand (weight, size over line height, family stack) and a letter-spacing line, for example font: 500 18px/1.5 ui-serif, Georgia, serif;. Copy puts both lines on the clipboard, which is the only thing the page touches, and says "Copied" to screen readers. If the browser refuses, for instance on a page not served over HTTPS, the button says it couldn't copy.
What stays local, and how do I wire it for real?
Everything. Apply to document restyles the drawn page in local state and says so in a polite status message; nothing is stored. In your app save the five choices (family id, weight, size, line height, letter spacing) with the document or the user's preferences in apply, and map them to the CSS variables or classes your editor uses. Reset to the saved values when the dialog closes, as Cancel does here.
Is it accessible from the keyboard?
Yes. The real dialog is named by its title and described by its sentence, focus is trapped while it is open, and Escape, Cancel and the close button return focus to the header button. The trigger is the button wrapped in DialogTrigger with asChild, and the dialog is controlled with open and onOpenChange. The copy shown on load moves no focus; Escape inside it and its close button close it and put focus back on the header button. The families are a radio group moved with the arrow keys, the weight and line height are toggle groups, and the size slider moves with the arrow keys, Home and End and says its value in pixels. The specimen is decoration and hidden from assistive technology; the CSS line states the values in text.
How does it look on a phone?
The specimen moves above the controls, so you see the effect of each change as you scroll the controls beneath it. The copy shown on load docks to the foot of the page as a bottom sheet with Apply to document pinned at the bottom. The real dialog keeps the Dialog's own phone layout: a sheet stuck to the bottom of the screen.