Skip to content
sysout.dev

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

#F7EBE7

Warm blush intended to feel like parchment or printed paper.

Letter / primary

#0A2D2E

Deep teal ink used by the icon and wordmark.

Shadow / depth

#A49E97

Warm grey used for logo depth and supporting boundaries.

Accent

#588A80

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 #0A2D2E on blush #F7EBE7 was strong at approximately 12.60:1.

  • Mid teal #588A80 on blush was only approximately 3.36:1, insufficient for normal-size text.

  • White on mid teal was approximately 3.92:1, so white was not a safe general onSecondary value.

  • Existing inline-code text was approximately 3.09:1 against 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

8.13.2

Gradle

8.13

Kotlin and Compose compiler plugin

2.3.20

Compose BOM

2026.06.00

Material 3 resolved version

1.4.0

Activity Compose

1.13.0

AppCompat

1.7.1

Core KTX

1.18.0

Navigation Compose

2.9.8

Java toolchain

17

Compile / target SDK

36 / 36

Minimum SDK

24

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

primary

#286462

#94D1CB

onPrimary

#FFFFFF

#003735

primaryContainer

#B0CCC8

#0F4B49

onPrimaryContainer

#0A2D2E

#B0CCC8

inversePrimary

#94D1CB

#286462

secondary

#506662

#B7CBC6

onSecondary

#FFFFFF

#22332F

secondaryContainer

#D3E7E2

#394E4A

onSecondaryContainer

#102D2A

#D3E7E2

tertiary

#7E5560

#EFB8C5

onTertiary

#FFFFFF

#492531

tertiaryContainer

#FFD9E0

#633B47

onTertiaryContainer

#32101A

#FFD9E0

background

#FFF8F6

#171211

onBackground

#211A19

#EDE0DD

surface

#FFF8F6

#171211

onSurface

#211A19

#EDE0DD

surfaceVariant

#E5DDDA

#4A4543

onSurfaceVariant

#484644

#CEC5C2

surfaceTint

#286462

#94D1CB

surfaceBright

#FFF8F6

#3E3735

surfaceDim

#E5D8D5

#171211

surfaceContainerLowest

#FFFFFF

#120D0C

surfaceContainerLow

#FBF1EF

#201817

surfaceContainer

#F5EBE8

#241C1B

surfaceContainerHigh

#EFE5E2

#2F2625

surfaceContainerHighest

#E9DFDC

#3A302F

inverseSurface

#372F2D

#EDE0DD

inverseOnSurface

#FBEDEA

#352D2B

error

#BA1A1A

#FFB4AB

onError

#FFFFFF

#690005

errorContainer

#FFDAD6

#93000A

onErrorContainer

#410002

#FFDAD6

outline

#797775

#938C89

outlineVariant

#CAC4C1

#4D4644

scrim

#000000

#000000

Fixed roles

Fixed roles deliberately keep the same values in light and dark application themes.

Role Value

primaryFixed

#B0CCC8

primaryFixedDim

#94B0AC

onPrimaryFixed

#00201F

onPrimaryFixedVariant

#0F4B49

secondaryFixed

#D3E7E2

secondaryFixedDim

#B7CBC6

onSecondaryFixed

#0A1F1C

onSecondaryFixedVariant

#394E4A

tertiaryFixed

#FFD9E0

tertiaryFixedDim

#EFB8C5

onTertiaryFixed

#31101A

onTertiaryFixedVariant

#633B47

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

#FFFDFC

#181716

Source text

#211A19

#EDE8E6

Cursor

#286462

#94D1CB

Selection handle

#286462

#94D1CB

Selection background

#B0CCC8

#0F4B49

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

#FFFDFC

#FFF8ED

#F4E6C9

#181716

Body text

#211A19

#2D241F

#382A1D

#EDE8E6

Heading

#0A2D2E

#163B3A

#4F3825

#B0D7D3

Link

#286462

#286462

#65482F

#94D1CB

Pressed link

#0A2D2E

#163B3A

#4F3825

#B0CCC8

Code canvas

#F3ECE9

#F2E5D2

#E6D2AA

#302C29

Code text

#3C2F2C

#3A2C24

#382A1D

#EDE8E6

Quote accent

#506662

#506662

#705538

#94D1CB

Divider

#797775

#81746A

#887457

#7A7470

Table stripe

#F8F1EE

#F8EDDC

#EEDDBB

#211F1D

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

#FFFFFF

Body text

#1F1B1A

Heading / pressed link

#123F3E

Link

#1B5F5D

Code canvas

#F3F0EF

Code text

#2E2927

Quote accent

#4E6460

Divider

#6F6A68

Table stripe

#F7F4F2

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

a379267docs: define visual theme architecture

1

Compose and build infrastructure without colour changes

96b6679build: prepare Compose for Material 3

2

Material 3 component compatibility baseline

4d0cc46refactor: migrate UI components to Material 3

3

Complete explicit Material 3 light and dark schemes

e976ed4style: define complete Material 3 colour schemes

4

Semantic application roles and system dark mode

d461207style: apply semantic application colours

5

Near-neutral document editor canvas

f2e76c2style: introduce the document editor canvas

6

System, Paper, Sepia, and Dark reader themes

b2ae061feat: add document reader themes

7

Explicit print palette, final role record, screenshots, and end-to-end PDF verification

docs: finalize visual design delivery

Two follow-up refinements were intentionally committed independently from the numbered stages:

  • 90cdd76feat: add representative reader preview sample

  • cad452fstyle: 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.