ganjoorandroid/design
Claude 64a67ddbbd
Set each couplet in a dim rounded card, on every screen size
Each line of verse now sits in its own soft card (surface-container-high,
12dp corners), so the eye finds where one couplet ends and the next
begins, and the couplet's chevron, actions and summary visibly belong to
it. On OLED black that step is all but black, so the card takes the next
one up there. Prose stays bare: a paragraph in a box reads as a quotation.

Text keeps at least 6:1 on the card in every theme. The phone layout is
otherwise unchanged.

Updates the Couplet notes, the stylesheet and wireframes 4-8, and adds
wireframe 9 for the phone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RhmDdrN5hgWrgRcFLAMsV6
2026-10-07 16:04:08 +00:00
..
assets Add the design system extracted from the app 2026-10-07 14:06:52 +00:00
components Set each couplet in a dim rounded card, on every screen size 2026-10-07 16:04:08 +00:00
wireframes Set each couplet in a dim rounded card, on every screen size 2026-10-07 16:04:08 +00:00
README.md Open reading settings in the left-hand panel on large screens 2026-10-07 14:51:12 +00:00
tokens.json Add the design system extracted from the app 2026-10-07 14:06:52 +00:00

Ganjoor design system

This folder holds the app's design system, extracted from its code:

  • tokens.json: colours for all six themes, type styles, spacing, radii and sizes.
  • components/: usage guidelines for each component, with a static preview.
  • assets/: the logo and icons as SVG.
  • wireframes/: the tablet and foldable layout, state by state.

The font files referenced in tokens.json (fonts/…) are the app's own, in app/src/main/res/font/, and the store screenshots are in fastlane/metadata/android/fa/images/. The previews use CSS variables named after the tokens. They render in the published design system artifact, which generates those variables from tokens.json.


Ganjoor is a quiet, long-form reader for Persian poetry, made for Persian, Urdu and Arabic script. The poem is the interface: everything else gets out of its way, says plainly what it does, and works offline. The palette comes from Persian tilework, turquoise with saffron accents, alongside warm paper tones for long sessions.

Built on Material 3 (Jetpack Compose). Tokens follow the Material colour roles one to one, so primary here is MaterialTheme.colorScheme.primary in the app.

Content fundamentals

  • Persian first. The interface defaults to Persian whatever the phone's locale, and also speaks Urdu and English. The app is called گنجور in every language. Languages are named in their own script (فارسی · اردو · English).
  • Right-to-left, always. The whole interface lays out and navigates RTL, even when the UI language is English. Directional icons mirror.
  • Plain, specific, honest. Say what happened and what to do next, without blaming the reader: "This isn't downloaded. Turn off offline mode, or download it first." is right; "Network error" is not. A setting's note says when it applies: "Applies to the dark themes; saves power on OLED screens".
  • Sentence case, no exclamation marks, no emoji. Labels are short verbs or nouns: Save this passage, Copy, Share, Download every poet. A state replaces the verb: Saved.
  • Credit and disclose. AI text is always labelled: "AI-generated by Ganjoor, not by the poet". The app is unofficial and says so in About. It is never presented as ganjoor.net's own.
  • Give the reader something useful. Listings show each poem's opening line under its title, because "Ghazal 237" tells you nothing.

Visual foundations

Colour and themes

Six themes, all the same roles. Light and Dark use turquoise primary with saffron secondary. Sepia and Sepia night are aged-paper schemes with walnut primary; the night one is dark without a blue cast. OLED black isn't a separate palette. It is a flag on either dark theme that moves only the surfaces towards black (in Oklab) and leaves text and accents exactly as they are. That gives the two themes Dark · OLED black and Sepia night · OLED black.

  • Page ground is surface. Primary text is on-surface; secondary text and icons are on-surface-variant.
  • primary is for what you can tap or what is active: tappable breadcrumbs, TextButtons, section labels, a saved bookmark, the pin mark. It is never used for running text.
  • Selected chips and portrait discs use secondary-container with on-secondary-container.
  • Poet cards use surface-container-highest; sheets use surface-container-low; the bottom search bar uses surface-tonal-3.
  • downloaded (green) appears only with the check-circle, so the state is never shown by colour alone.
  • Every text pair named in a token's notes meets 4.5:1 in all six themes. Sepia's outline is 3.3:1 on surface, which passes for borders only.

Typography

  • Poems are always Noto Naskh Arabic or Noto Nastaliq Urdu, whatever the UI language. Use poem-naskh by default and poem-nastaliq when the reader picks it. The reader chooses a size from 14 to 40px (default 22) and a weight of Regular, Medium, Semibold or Bold, because thin naskh strokes wash out on a lit screen, especially in dark themes. Leading is 1.8× for naskh and 2.4× for nastaliq, whose diagonal stacking and deep descenders would otherwise clip.
  • The Persian and Urdu interface uses the Material 3 type scale in Naskh (title-medium, body-small…).
  • The English interface uses the same scale in Libron, a reading serif (en-title-medium…). Libron never sets a poem.

Layout and spacing

Values are the Compose dp values written in the code (1dp = 1px): space-20 for the reader column and settings sheet sides, space-12 for list sides and grid gutters, space-8 as the default gap, and space-6 above and below each couplet. Touch targets are touch (48px). The poet grid is adaptive with columns of at least poet-grid-min.

Couplets stack on a phone: the first hemistich aligns to the start, the second to the end. Prose (Golestan, Nowruznameh) is justified. On large screens the hemistichs sit side by side (see ColumnBrowser).

One-handed reach: the poets search lives in a bottom bar, and the settings open as a bottom sheet.

Shape, elevation, motion

Shapes are Material's: radius-sm for chips, radius-md for cards, radius-xl for sheet tops and radius-full for buttons and portrait discs. There are no drop shadows. Elevation is tonal: surface containers step up in tone. Motion is Material's default. The one custom transition is ColumnBrowser's column slide, 400ms emphasized-decelerate.

States

Pressed and hovered states use Material's state layers. A focus ring is 2px primary with a 2px offset, at least 3:1 on every surface. Show a selection with a fill (a chip or the selected pane row) as well as a check mark or a change of word.

Imagery

Poet portraits come from Ganjoor and are cropped into a circle on secondary-container. The poet's initial sits underneath, so a missing portrait never leaves a hole. There are no other illustrations.

Iconography

Material Icons in the filled style, at 24px, tinted with colour roles (see the Icons group). Use the standard Android share glyph wherever there is sharing, because people recognise it before reading the label. The brand mark is the eight-pointed khatam star (see Logos). There is no wordmark: گنجور is set in Naskh.

Large screens

Tablets and unfolded foldables use ColumnBrowser. Tapping a poet collapses the home grid into a narrow poets column on the far right (portrait with the name underneath). To its left come the poet's books, then the chapters or poems of the open book, each column scrolling on its own. Older columns narrow as new ones open, so the poem stays near the middle, and a top-bar button hides the columns when the reader wants to focus on the poem. The dictionary and reading settings open in a panel on the left, beside the poem rather than over it. Phones are unchanged.