A Compose Multiplatform theme library inspired by the classic Napkin Look & Feel for Java Swing: every component looks hand-drawn on a napkin — wobbly felt-tip outlines, handwritten headlines, Comic Sans body text, red-pencil scribbles, highlighter swipes, and post-it notes.
Like the Swing original, it is great for keeping provisional UIs looking provisional — a mockup that looks unfinished never gets mistaken for a finished product. It is also just plain whimsical.
| Light ("napkin") | Dark ("blackboard") |
|---|---|
![]() |
![]() |
- Desktop JVM (macOS, Windows, Linux)
- Android (minSdk 24)
- iOS (arm64, simulator arm64)
NapkinTheme { // or NapkinTheme(darkTheme = true) for blackboard
Box(Modifier.fillMaxSize().napkinPaper()) {
Column {
Text("Hello!", style = MaterialTheme.typography.headlineMedium)
NapkinButton(onClick = { /* ... */ }) { Text("Click me") }
}
}
}NapkinTheme wraps MaterialTheme (Material 3), so stock Material
components pick up the palette and comic type scale automatically; the
Napkin* components add the hand-drawn geometry on top.
| Composable | What you get |
|---|---|
NapkinButton |
Sketched box at a slight tilt; fills with highlighter while pressed |
NapkinCheckbox |
Wobbly ink square; checking animates a green marker tick that overshoots the corner |
NapkinRadioButton |
Wobbly ink ring with a scribbled dot |
NapkinSwitch |
Doodled track with a sliding ink knob |
NapkinTextField |
Sketch-boxed input with a red-pencil cursor and pencilled-in placeholder |
NapkinLinearProgress |
Sketched track filled with a red-pencil zigzag scribble |
NapkinCard |
Slightly whiter paper with its own faint tilt |
NapkinDivider |
A single wobbly pen stroke |
NapkinPostIt |
A yellow post-it at a jaunty angle (stays yellow on the blackboard) |
Modifier.napkinPaper()— the napkin itself: warm paper with fiber specksModifier.napkinSketchBorder(...)— hand-sketched outline around anythingModifier.napkinHighlight()— translucent highlighter swipe behind contentModifier.napkinUnderline()— wobbly hand-drawn underline
All sketch geometry is generated by Sketcher from a per-component random
seed remembered across recompositions, so outlines wobble but never dance.
Tune the look via NapkinTheme(sketch = SketchStyle(wobble = 2.5.dp, ...)).
Two families, mirroring the drawing tools:
- Headlines/titles use a felt-pen handwriting face. The Swing original used Felt Tip Roman, which is commercial; the closest open equivalent, Patrick Hand (SIL OFL), is bundled.
- Body/labels use Comic Sans. The genuine Comic Sans MS is a
proprietary Microsoft font that cannot be redistributed, so on desktop it
is loaded from the OS where installed (macOS, Windows, Linux with
msttcorefonts); Android and iOS fall back to the bundled Comic Neue (SIL OFL), the Comic Sans revival.
Splitting the type scale is deliberate, for two reasons:
- Faithfulness to the original. Napkin LAF didn't actually use Comic Sans — it used Felt Tip Roman, a felt-pen handwriting face, for its scrawled emphasis text alongside a tamer face for regular text. Using the felt-pen family for headings and Comic Sans for body text keeps both traditions alive.
- Typography. The two faces have different strengths at different sizes. Patrick Hand is expressive but ships in a single weight with no italics, has a small x-height, and gets hard to read at 12–14sp in dense UI text; it shines at headline sizes, where it reads as "someone wrote a heading on this napkin." Comic Sans was literally designed for small on-screen body text, so it stays legible in labels, buttons, and paragraphs — and its bold/italic variants matter for body and label styles. The contrast also gives you visual hierarchy for free: marker pen for headings, neat hand for notes, just like a real napkin sketch.
Both slots are plain FontFamily parameters on NapkinTheme, so your app
can override either or both:
// Comic Sans for everything, headlines included:
NapkinTheme(handwritingFontFamily = rememberComicFontFamily()) { /* ... */ }
// Felt pen for everything, body text included:
NapkinTheme(comicFontFamily = rememberFeltPenFontFamily()) { /* ... */ }
// Or bring your own faces:
NapkinTheme(
handwritingFontFamily = myMarkerFamily,
comicFontFamily = myBodyFamily,
) { /* ... */ }Font attribution lives in napkin/FONT-LICENSES.txt.
./gradlew :napkin:assemble # library artifacts for all targets
./gradlew :demo:run # desktop demo gallery
./gradlew :napkin:jvmTest # sketch-geometry tests
./gradlew :demo:jvmTest # renders the gallery to demo/build/render/*.png
