Visual Design and Theme Architecture
Starting point, final token values, semantic mappings, accessibility requirements, and completed Material 3 delivery record for asciitor.
This document records where asciitor’s visual system started, what was learned during the colour review, and the colour systems delivered for application chrome, editing, reading, and printing.
It complements the brand guidelines. The brand guidelines define the identity assets and source palette; this document defines how that identity becomes an accessible application colour system.
- Status
-
Implemented and verified through Stage 7
- Last reviewed
-
July 2026
Purpose and scope
The objective was not to redesign asciitor or add unrelated functionality. It was to establish a deliberate, accessible, reusable Material 3 colour language while preserving the application’s quiet printed-page identity.
The completed review covers:
-
Complete explicit Material 3 light and dark colour schemes.
-
Semantic token use in production screens.
-
A dedicated full-screen document-editor canvas.
-
Reader-specific System, Paper, Sepia, and Dark themes.
-
A deterministic white-paper print palette.
-
Contrast, selection, focus, and non-colour interaction cues.
-
Representative AsciiDoc content for visual validation.
-
Independently testable delivery commits.
Navigation and feature changes, layout redesign, dynamic wallpaper colours, and an AMOLED-black reader theme were outside this delivery.
Starting point
Brand source palette
The original brand palette contains four colours:
| Source role | Value | Original purpose |
|---|---|---|
Background |
|
Warm blush intended to feel like parchment or printed paper. |
Letter / primary |
|
Deep teal ink used by the icon and wordmark. |
Shadow / depth |
|
Warm grey used for logo depth and supporting boundaries. |
Accent |
|
Mid teal used as a supporting interface and preview colour. |
These colours are identity seeds and references, not four literal Material role assignments.
Original implementation
Before this work, the application used Compose Material 2 and a light-only palette created directly in the root composable.
The background and surface were both the literal blush #F7EBE7, primary was used for most foregrounds and decorations, the full-screen editor was a filled Material field, preview CSS hard-coded the light brand palette, and print inherited most screen-preview colours.
The main findings were:
-
Deep teal
#0A2D2Eon blush#F7EBE7was strong at approximately12.60:1. -
Mid teal
#588A80on blush was only approximately3.36:1, insufficient for normal-size text. -
White on mid teal was approximately
3.92:1, so white was not a safe generalonSecondaryvalue. -
Existing inline-code text was approximately
3.09:1against its code background. -
Primary was overused for ordinary content and passive decoration.
-
No selected-state container role or dark application scheme existed.
-
The filled full-screen editor looked like an oversized form or overlay rather than a document canvas.
-
Preview and print lacked independent semantic colour contracts.
Final theme architecture
| Layer | Responsibility | Final direction |
|---|---|---|
Brand constants |
Icon, wordmark, and marketing identity |
Literal identity colours remain available where their background is controlled. |
Material application theme |
App bars, drawer, tabs, buttons, settings, menus, dialogs, snackbars, and system-facing UI |
Complete explicit light and dark Material 3 schemes. |
Document editor |
Editable source, cursor, handles, and selection |
A quiet near-neutral canvas independent from filled form-field containers. |
Reader theme |
Preview prose, headings, links, code, quotes, tables, and dividers |
System, Paper, Sepia, and Dark reader modes; Paper is the fresh-install default. |
Print theme |
PDF and physical printing |
Deterministic white paper that never inherits a reader or system dark background. |
The Material theme governs the application around a document. The editor and reader themes govern the writing and reading surfaces. Print is independent from both.
Final implementation baseline
| Item | Delivered version |
|---|---|
Android Gradle Plugin |
|
Gradle |
|
Kotlin and Compose compiler plugin |
|
Compose BOM |
|
Material 3 resolved version |
|
Activity Compose |
|
AppCompat |
|
Core KTX |
|
Navigation Compose |
|
Java toolchain |
|
Compile / target SDK |
|
Minimum SDK |
|
The Compose migration deliberately retained the Material 2 dependency only for compatibility with APIs still used by the application; all production theme and component styling introduced by this project uses Material 3 roles.
Design principles and production mappings
Roles are semantic
Colour is assigned according to what a component communicates. Different colours require a stable semantic reason, not a desire for visual variety.
Primary is selective
Primary is reserved for the principal document action, active tab emphasis, interactive links and actions, and limited brand expression. Ordinary headings, filenames, passive icons, body text, and dividers use surface or outline roles.
Selection uses a container and shape
Selected drawer destinations and segmented settings options use primaryContainer with onPrimaryContainer.
The surrounding shape, position, and selected indicator prevent selection from relying on hue alone.
Surfaces express hierarchy
The app background and app bar share the warm-neutral application surface.
The drawer uses surfaceContainerLow, menus use surfaceContainerHigh, and ordinary card-like controls remain close to the base surface.
The restrained differences are intentional: surface roles express grouping and hierarchy rather than new semantic categories.
Boundaries use outline roles
Strong control borders use outline; quiet dividers use outlineVariant.
Primary is not the default divider or passive boundary colour.
Foregrounds match their containers
Primary actions use onPrimary, selected containers use onPrimaryContainer, ordinary content uses onSurface, supporting content uses onSurfaceVariant, and destructive content uses the error family.
Hard-coded white or black is not substituted for these semantic foreground roles.
Complete Material 3 role values
All roles exposed by the selected Material 3 ColorScheme are explicitly assigned.
Unused roles are still defined by asciitor rather than inherited from Compose-generated scheme defaults.
Light and dark roles
| Role | Light | Dark |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Fixed roles
Fixed roles deliberately keep the same values in light and dark application themes.
| Role | Value |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Document editor
The filled Material field was replaced by a foundation BasicTextField while preserving document persistence, keyboard handling, dimensions, and unsaved-state behavior.
The editor now reads as a continuous document surface instead of a dialog overlay or oversized form input.
| Editor role | Light | Dark |
|---|---|---|
Canvas |
|
|
Source text |
|
|
Cursor |
|
|
Selection handle |
|
|
Selection background |
|
|
Reader themes
The preview WebView receives explicit CSS variables for its native background, HTML canvas, body text, headings, links and pressed links, code, quotes, dividers, and alternating table rows. This prevents automatic colour inversion and keeps the native and rendered surfaces synchronized. Links remain underlined so clickability does not depend on colour alone.
Paper is the default for a new install, a missing value, or an invalid stored value. An explicitly saved System preference remains System and follows the system light/dark appearance.
| Reader role | System light | Paper | Sepia | Dark / System dark |
|---|---|---|---|---|
Canvas |
|
|
|
|
Body text |
|
|
|
|
Heading |
|
|
|
|
Link |
|
|
|
|
Pressed link |
|
|
|
|
Code canvas |
|
|
|
|
Code text |
|
|
|
|
Quote accent |
|
|
|
|
Divider |
|
|
|
|
Table stripe |
|
|
|
|
The inline Settings reading sample remains a compact single sentence so it does not create a nested vertical scroll area inside Settings. An adjacent action opens a representative AsciiDoc document from the application assets in the normal full-screen preview. The bundled document contains a title, emphasis, inline code, a link, a list, an admonition, a Kotlin source block, a quote, and a table. This keeps theme comparison close to the controls while reserving document scrolling and fling behavior for the existing preview viewport. Returning from the bundled sample restores the previous Settings list position so theme adjustments and full-document checks form a short comparison loop.
Deterministic print theme
Print colours are injected into the same owned HTML/CSS pipeline inside @media print.
They override every reader theme, including Dark and Sepia.
| Print role | Value |
|---|---|
Paper |
|
Body text |
|
Heading / pressed link |
|
Link |
|
Code canvas |
|
Code text |
|
Quote accent |
|
Divider |
|
Table stripe |
|
The Android print service produced a two-page, Letter-size PDF from the representative document. Both pages were rasterized and visually inspected: paper remained white; body text, teal headings and underlined links were readable; code, quote, admonition, table stripes, and boundaries remained distinct; and no screen-theme canvas leaked into the output.
Accessibility and validation
Validation treated each foreground and background as a pair and covered:
-
Automated contrast checks for application, editor, reader, and print pairs.
-
Explicit role-completeness tests for both Material schemes.
-
Selected states with container and shape cues.
-
Underlined preview links.
-
Cursor, selection handle, and selection-background visibility.
-
Light and dark application modes.
-
System, Paper, Sepia, and Dark reader modes.
-
Representative preview content and independent sample scrolling.
-
Home, Settings, editor, preview, menus, and Android print UI on an API 35 AVD.
-
Unit tests, lint, debug assembly, APK installation, and launch smoke tests.
-
Owner physical-device review of the application and document-theme changes.
Asciitor is small enough that a separate Visual Lab is unnecessary. Its production screens and the representative Settings reading sample are the final comparison surfaces.
Completed delivery stages
| Stage | Delivered scope | Commit |
|---|---|---|
0 |
Theme architecture and staged plan |
|
1 |
Compose and build infrastructure without colour changes |
|
2 |
Material 3 component compatibility baseline |
|
3 |
Complete explicit Material 3 light and dark schemes |
|
4 |
Semantic application roles and system dark mode |
|
5 |
Near-neutral document editor canvas |
|
6 |
System, Paper, Sepia, and Dark reader themes |
|
7 |
Explicit print palette, final role record, screenshots, and end-to-end PDF verification |
|
Two follow-up refinements were intentionally committed independently from the numbered stages:
-
90cdd76—feat: add representative reader preview sample -
cad452f—style: make paper the default reader theme
Deferred refinements
The application chrome and the reader/editor canvas now have different semantic responsibilities and therefore can have visibly different backgrounds. That separation is correct, but the transition can draw attention to the chrome while reading or editing.
A future document-focus or max-screen mode may reduce application UI to a compact toolbar for navigation and document actions. This is an interaction and layout enhancement, not a reason to revert the independent Material, editor, reader, or print colour systems.
An AMOLED-black reader mode can be considered later only if usage and device testing justify it.