Interactive web component for testing and previewing fonts. No dependencies. Built for Fountain but works anywhere.
- Live text preview with editable text
- Font style/weight variant selector
- Adjustable size, line height, and letter spacing
- Variable font axis sliders
- OpenType feature toggles
- Custom sample texts
- Lazy font loading via font-loader (optional)
- Themeable via CSS custom properties and
::part() - i18n support
<font-tester font-family="my-font" controls="text-controls,font-size,line-height,letter-spacing,font-style,opentype">
<!-- Font style variants -->
<font-style name="Regular" family="my-font" weight="400" style="normal" default></font-style>
<font-style name="Italic" family="my-font" weight="400" style="italic"></font-style>
<font-style name="Bold" family="my-font" weight="700" style="normal"></font-style>
<!-- OpenType features -->
<opentype-feature code="kern" name="Kerning" default></opentype-feature>
<opentype-feature code="liga" name="Ligatures" default></opentype-feature>
<opentype-feature code="smcp" name="Small Caps"></opentype-feature>
<!-- Sample texts (first is shown by default; named ones appear in the dropdown) -->
<sample-text>The quick brown fox jumps over the lazy dog</sample-text>
<sample-text name="Alphabet">ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz</sample-text>
<sample-text name="Numbers">0123456789</sample-text>
</font-tester>For variable fonts, use var-axes in controls and declare axes via <font-variation-axis> markers instead of <font-style> variants. The two are mutually exclusive.
<font-tester font-family="my-variable-font" controls="text-controls,font-size,line-height,var-axes">
<font-variation-axis tag="wght" name="Weight" min="100" max="900" default="400"></font-variation-axis>
<font-variation-axis tag="wdth" name="Width" min="75" max="125" default="100"></font-variation-axis>
<font-variation-axis tag="OPSZ" name="Optical size" min="6" max="72" default="14" step="0.1"></font-variation-axis>
</font-tester>Each axis renders as a range slider. All axes compose into a single font-variation-settings value applied to the preview text.
Axis attributes: tag (4-char axis tag, required), name (label), min, max, default, step.
Layout is controlled via CSS custom properties on var-axes-controls:
font-tester var-axes-controls {
--axis-flex-basis: 200px; /* min width per axis — controls how many fit per row */
--axis-gap: 20px;
}Add the fit-width attribute to automatically scale the font size so the preview text fills the full width of the display area. Useful for single-word or headline display testing.
<font-tester font-family="my-font" fit-width="each">
<sample-text>Stockholm</sample-text>
</font-tester>The font size is calculated using the browser's layout engine, so letter-spacing, variable axes, and OpenType features are all accounted for. A 2px safety margin is applied to prevent overflow.
Two modes are available via the attribute value:
| Value | Behaviour |
|---|---|
each (or bare fit-width) |
Recalculates on every font load, including style/weight changes |
once |
Fits once on the first font load, then leaves the size alone |
once is useful when you want the initial size set automatically but still want full control of the font-size slider afterwards.
If a font-size slider is included in controls, it stays in sync — it updates to show the calculated value and can still be used to manually override it. With each, the fit is restored on the next font change.
fit-width works with or without text-controls. If text-controls is omitted, the first <sample-text> is shown directly without a dropdown.
Set starting values for the typography controls directly on <font-tester>:
<font-tester
font-family="my-font"
font-size="64px"
line-height="1.2"
letter-spacing="0.02em"
text-align="center"
direction="rtl"
>These are read once at connect time — sliders initialise at these values and the display renders with them applied. They are not reactive after mount.
Comma-separated list of controls to show. Omit the attribute entirely to show all controls.
| Value | Control |
|---|---|
text-controls |
Uppercase toggle, text direction, text alignment |
font-size |
Font size slider |
line-height |
Line height slider |
letter-spacing |
Letter spacing slider |
font-style |
Style/weight variant dropdown (static fonts) |
opentype |
OpenType features dialog |
var-axes |
Variable font axis sliders |
<script type="module" src="font-tester.min.js"></script>Register fonts in CSS as usual. Pass the family name via the font-family attribute:
<style>
@font-face {
font-family: 'my-font';
src: url('/fonts/my-font.woff2') format('woff2');
}
</style>
<font-tester font-family="my-font">...</font-tester>font-loader loads fonts on demand via the FontFace API as testers enter the viewport — no @font-face CSS needed.
<script type="importmap">
{
"imports": {
"font-loader": "/js/font-loader.min.js"
}
}
</script>
<script type="module" src="/js/font-loader.min.js"></script>
<script type="module" src="/js/font-tester-with-loader.min.js"></script>The font-tester-with-loader.min.js bundle includes font-tester-loader.js, which bridges the two libraries. Use font-tester.min.js (without the loader) if you're using @font-face.
All visual styling is exposed via CSS custom properties and ::part() selectors. Copy font-tester-theme.css as a starting point:
font-tester {
--container-max-width: 1200px;
--divider-color: #e0e0e0;
/* ... */
}
font-tester font-display {
--display-bg: #f9f9f9;
--text-color: #111;
/* ... */
}
font-tester text-controls::part(button) {
border-radius: 0;
}See font-tester-theme.css for the full reference.
Parts can be targeted with font-tester::part(name) or, for nested parts, via the sub-component directly (e.g. font-tester text-controls::part(button)).
Sections (font-tester::part())
| Part | Element |
|---|---|
controls-section |
Controls bar (text controls, font style, OpenType) |
style-controls-section |
Sliders section |
var-axes-section |
Variable axes section |
display-section |
Font preview area |
Text controls (font-tester::part())
| Part | Element |
|---|---|
button |
All buttons |
uppercase-button |
Uppercase toggle |
direction-group |
LTR/RTL radio group |
alignment-group |
Alignment radio group |
ltr-button / rtl-button |
Direction buttons |
align-left-button / align-center-button / align-right-button |
Alignment buttons |
Sample text selector (font-tester::part())
| Part | Element |
|---|---|
sample-text-selector |
The <sample-text-selector> host |
sample-select |
The <select> dropdown |
Font style selector (font-tester::part())
| Part | Element |
|---|---|
font-style-selector |
The <font-style-selector> host |
style-wrapper |
Wrapper div |
style-label |
Label |
style-select |
The <select> dropdown |
OpenType features (font-tester::part())
| Part | Element |
|---|---|
opentype-features |
The <opentype-features> host |
button / features-button |
Trigger button |
features-panel |
Panel |
features-list |
<ul> inside the panel |
feature-item |
Each <li> |
feature-switch |
Each switch button |
Style controls / sliders (font-tester::part())
| Part | Element |
|---|---|
control-item |
Each slider row |
font-size-item / line-height-item / letter-spacing-item |
Specific rows |
label |
All labels |
font-size-label / line-height-label / letter-spacing-label |
Specific labels |
slider |
All range inputs |
font-size-slider / line-height-slider / letter-spacing-slider |
Specific sliders |
value-display |
All value readouts |
font-size-value / line-height-value / letter-spacing-value |
Specific readouts |
Variable axes (font-tester::part())
| Part | Element |
|---|---|
var-axes-controls |
The <var-axes-controls> host |
axis-control |
Each axis row |
label |
Axis labels |
slider |
Axis sliders |
value-display |
Axis value readouts |
Parts can also be used to hide controls that are enabled in controls but should not be shown in a particular instance:
font-tester::part(sample-text-selector) {
display: none;
}UI labels can be translated via an inline or external JSON file. See FONT-TESTER-I18N-README.md for setup and the full list of translation keys.
<script id="font-tester-i18n" type="application/json">
{
"sv": {
"textControls": { "uppercaseButton": "Versaler" }
}
}
</script>
<font-tester lang="sv">...</font-tester>Two reasons. One is to make it feel native to HTML. The other is that each instance gets its own shadow root, so the browser recalculates styles in isolation — with 20+ instances on a page that makes a meaningful difference.
MIT
