1. Purpose

Canon uses a carefully designed color scheme and symbol system to communicate the nature and severity of differences in document comparisons.

2. Diff classification

Canon classifies differences into three distinct categories, each with unique visual markers and directional colors.

Type Marker Color Meaning

Formatting-only

[ (removed)
] (added)

Dark gray (removed)
Light gray (added)

Purely cosmetic differences with no semantic meaning.
Only whitespace, line breaks, or indentation differ.
Content is semantically identical.

Informative

< (removed)
> (added)

Blue (removed)
Cyan (added)

Differences that don’t affect current match results.
May become normative with different match option settings.
Provides context about ignored differences.

Normative

- (removed)
+ (added)

Red (removed)
Green (added)

Semantic differences that affect match results.
Requires developer attention.
Represents actual content changes.

Mixed

*

Yellow

Line contains multiple separate changed regions.
Both old and new lines are modified.
Appears on both OLD and NEW lines.

2.1. Classification hierarchy

The classification follows a strict hierarchy:

formatting < informative < normative

A difference is classified using the highest applicable category:

normative

the differences leads to semantic changes that are considered normative

informative

the differences leads to semantic changes that are considered informative

formatting-only

the differences do not cause any semantic difference

3. Formatting-only detection

Formatting-only differences are detected through aggressive normalization:

  1. Collapse all whitespace (spaces, tabs, newlines) to single space

  2. Strip leading and trailing whitespace

  3. Compare normalized content

If the normalized content is identical, the difference is classified as formatting-only.

Example 1. Example: Formatting-Only Difference
  5|   5[ | <p class="section-break"><br clear="all" class="section"></p>
   |   5] | <p class="section-break"><br clear="all" class="section"><div class="WordSection2">
  6|   6] | <div class="WordSection2">

These lines differ only in how content is split across lines. The semantic content (when whitespace is normalized) is identical, so they show as formatting-only with [ (dark gray) for removed and ] (light gray) for added lines.

4. Color Coding

4.1. By Difference Type

4.1.1. Formatting-Only (Gray Tones)

Formatting-only changes use directional gray tones to indicate low visual priority:

  • Dark gray ([): Formatting-only removal

  • Light gray (]): Formatting-only addition

  • Purely cosmetic

  • Safe to ignore

  • No semantic impact

4.1.2. Informative (Blue Tones)

Informative changes use blue tones to indicate medium visual priority:

  • Blue (<): Informative removal

  • Cyan (>): Informative addition

  • Context-dependent

  • Currently ignored

  • May matter with different settings

4.1.3. Normative (Red/Green)

Normative changes use red/green for high visual priority:

  • Red (-): Content removed (normative difference)

  • Green (+): Content added (normative difference)

  • Requires attention

  • Affects comparison result

5. Symbol Meanings

5.1. Line-Level Markers

5.1.1. Formatting Markers (Gray)

  • [ - Line removed (formatting-only difference, dark gray)

  • ] - Line added (formatting-only difference, light gray)

5.1.2. Informative Markers (Blue)

  • < - Line removed (informative difference, blue)

  • > - Line added (informative difference, cyan)

5.1.3. Normative Markers (Red/Green)

  • - - Line removed (normative difference, red)

  • + - Line added (normative difference, green)

5.1.4. Mixed Marker (Yellow)

  • * - Line with multiple separate changed regions (yellow)

    • Appears on BOTH old and new lines when changes are interleaved.

    • A simple word replacement (e.g., "John" → "Jane") uses -/+, not *.

    • Only used when there are 2+ separate changed regions in the same line.

5.1.5. Context Marker

  • ` ` (space) - Unchanged line (context)

5.2. Line Numbers

Line numbers are displayed in yellow with pipe separators:

 123|  45+ | content here
^^^^  ^^
old   new
line  line
  • Left number: Line number in original file

  • Right number: Line number in modified file

  • Empty space (` `) when line doesn’t exist in that file

6. Visual Examples

6.1. Formatting-Only Change (Gray Tones)

  5|   5[ | <p>Content that spans        (dark gray)
   |   5] | <p>Content that spans multiple lines</p>    (light gray)
  6|   6] | multiple lines</p>           (light gray)

6.2. Informative Change (Blue Tones)

  10|  10< | <div  class="test">         (blue)
    |  10> | <div class="test">          (cyan)

6.3. Normative Change (Red/Green)

  15|  15- | <title>Old Title</title>    (red)
    |  15+ | <title>New Title</title>    (green)

6.4. Mixed Context

  20|  20  | <body>
  21|  21[ |   <p>Hello</p>                                (dark gray)
    |  21] |   <p>Hello</p><span>World</span>             (light gray)
  22|  22] |   <span>World</span>                         (light gray)
  23|  23- | <footer>2023</footer>                        (red)
    |  23+ | <footer>2024</footer>                        (green)
  24|  24  | </body>

This example shows:

  • Line 20, 24: Unchanged context (space marker)

  • Lines 21-22: Formatting-only change ([ dark gray, ] light gray)

  • Line 23: Normative change (- red, + green)

7. Configuration

Colors can be disabled for terminal compatibility:

result = Canon.compare(doc1, doc2, format: :xml, use_color: false)

When colors are disabled:

  • Markers remain (` [ ] < > - +`)

  • All text appears in terminal default color

  • Line numbers and pipes remain visible

7.1. Tree Diff Symbols

In by-object diff mode, box-drawing characters create a visual tree structure:

Symbol Meaning

├──

Tree branch (child element)

└──

Last tree branch (last child)

Tree continuation (vertical line)

-

Deletion marker (red)

+

Addition marker (green)

~

Change marker (yellow)

Example 2. Example: Tree structure with symbols
root
├── - item: "Old value"              # Deleted (red -)
├── + item: "New value"               # Added (green +)
└── ~ title                           # Modified (yellow ~)
    ├── - "Old Title"                 # Old value
    └── + "New Title"                 # New value

8. Character Visualization Symbols

When use_color: true, Canon visualizes invisible characters using Unicode symbols. See Character Visualization for the complete mapping.

Common examples:

Character Symbol Unicode

Regular space

U+2591 (Light Shade)

Tab

U+21E5 (Rightwards Arrow to Bar)

Non-breaking space

U+2423 (Open Box)

Line feed

U+21B5 (Downwards Arrow with Corner Leftwards)

Zero-width space

U+2192 (Rightwards Arrow)

9. Namespace Rendering

9.1. Namespace Display Format

Canon makes namespace information explicit in diff output, particularly when comparing XML elements and attributes:

Namespace State Display Format

Empty namespace

ns:[]

Populated namespace

ns:[http://example.com]

Example 3. Example: Namespace display in diff output
# Element with no namespace
- <element ns:[{blank}]>content</element>

# Element with namespace
+ <element ns:[http://example.com]>content</element>

9.2. Namespace Mismatch Classification

When elements or attributes differ, Canon classifies the mismatch type:

Mismatch Type Message Format

Same namespace, different name

mismatched element name: 'oldname' vs 'newname' (ns:[http://example.com])

Different namespace, same name

mismatched element namespace: 'element' (ns:[] vs ns:[http://example.com])

Both name and namespace differ

mismatched element name and namespace: 'old' (ns:[uri1]) vs 'new' (ns:[uri2])

Example 4. Example: Namespace mismatch messages
# Name differs, same namespace
mismatched element name: 'item' vs 'product' (ns:[http://shop.com])

# Namespace differs, same name
mismatched element namespace: 'element' (ns:[{blank}] vs ns:[http://example.com])

9.3. Attribute Namespace Rendering

The same namespace rendering applies to attributes:

Example 5. Example: Attribute namespace in diffs
# Attribute with no namespace
- <element attr="value" ns:[{blank}]/>

# Attribute with namespace
+ <element ns:attr="value" ns:[http://ns.com]/>

# Mismatch message
mismatched attribute namespace: 'id' (ns:[{blank}] vs ns:[http://example.com])

10. Header and Metadata Colors

Element Color Purpose

Section headers

Cyan + Bold

"Visual Diff:", "Line-by-line diff:", etc.

Algorithm name

Cyan + Bold

"Algorithm: SEMANTIC TREE DIFF", "Algorithm: DOM DIFF"

Success messages

Green + Bold

"✅ Files are semantically equivalent"

Separators

Cyan + Bold

"━━━━" (Unicode box drawing)

Legend headers

Cyan + Bold

"Character Visualization Legend:"

Category names

Yellow + Bold

"Common Whitespace:", "Unicode Spaces:", etc.

11. Normative vs Informative Classification

The color distinction between normative and informative diffs is crucial:

11.1. Normative Diffs (Red/Green)

Definition: Differences that cause files to be non-equivalent based on the current match configuration.

When shown: * Match dimension is set to :strict or :normalize * The difference persists even after applying normalization

Example 6. Example: Normative attribute difference
# With attribute_order: :strict
   5|   -| <item id="a" name="foo"/>    # Red (normative)
     |  6+| <item name="foo" id="a"/>    # Green (normative)

The attribute order difference is normative because attribute_order: :strict treats it as significant.

11.2. Informative Diffs (Cyan)

Definition: Differences that don’t affect file equivalence based on the current match configuration.

When shown: * Match dimension is set to :ignore or :normalize handled the difference * The difference exists but is ignored for equivalence purposes * Shown with show_diffs: :all or show_diffs: :informative

Example 7. Example: Informative whitespace difference
# With structural_whitespace: :ignore
   5| ~~ | <item>                       # Cyan (informative)
     | ~~ |   <name>Widget</name>        # Extra indentation ignored

The whitespace difference is informative because structural_whitespace: :ignore means it doesn’t affect equivalence.

12. Color Control

12.1. Theme System

Canon supports multiple color themes that adapt the diff display to your terminal background and personal preference. Four built-in themes are available:

  • :light — Light terminal backgrounds

  • :dark — Dark terminals (default)

  • :retro — Amber CRT, low blue light, accessibility

  • :claude — Claude Code diff style with red/green backgrounds

See Diff display themes for full theme documentation.

12.2. Automatic Color Detection

Canon automatically detects whether the terminal supports color output:

  • TTY detection: Colors are only enabled when output is to a terminal (not when piped to a file)

  • NO_COLOR support: If the NO_COLOR environment variable is set (per https://no-color.org/), colors are disabled

  • Terminal capability detection: Checks environment variables like TERM and COLORTERM

  • CI environment awareness: Detects and adapts to CI systems (GitHub Actions, Travis CI, etc.)

This means colors work automatically in most scenarios without manual configuration.

12.3. Enabling/Disabling Colors

Colors can be explicitly controlled using the use_color option:

Example 8. Using automatic detection (default)
# Auto-detects color support (default)
canon diff file1.xml file2.xml
Example 9. Explicit control
# Force enable colors
canon diff file1.xml file2.xml --color

# Force disable colors
canon diff file1.xml file2.xml --no-color
Ruby API
# Auto-detect (default)
Canon.compare(file1, file2, format: :xml)

# Explicit control
Canon.compare(file1, file2, format: :xml, use_color: false)

=== NO_COLOR Environment Variable

Per the NO_COLOR standard, setting the NO_COLOR environment variable will disable colors:

# Disable colors (respected by Canon and other NO_COLOR-aware tools)
export NO_COLOR=1

# Run canon - colors will be disabled
canon diff file1.xml file2.xml

The NO_COLOR variable always takes precedence over other color settings.

== Diff Display Modes

Canon supports two display modes for changed lines, controlled by the diff_mode option:

=== Separate-Line Mode (default)

Each changed line is shown as two lines — old on top, new on bottom:

   2|    - | <p>John░Doe</p>
    |   2+ | <p>Jane░Doe</p>
  • Old line: line number on left, - marker, full content

  • New line: line number on right, + marker, full content

  • Changes are highlighted with colors (red for old, green for new)

=== Inline Mode

Old and new content shown on the same line, with inline highlighting:

  • When color is ON: removed text appears in red, added text in green

  • When color is OFF: removed text has strikethrough (\e[9m), added text has underline (\e[4m)

  • Both sides of the change are visible simultaneously

result = Canon::Comparison.equivalent?(xml1, xml2,
  verbose: true,
  diff_mode: :inline
)

=== Legacy Terminal Mode

For terminals without ANSI support, Canon forces separate-line mode with no color codes:

result = Canon::Comparison.equivalent?(xml1, xml2,
  verbose: true,
  legacy_terminal: true
)

Legacy terminal mode automatically sets diff_mode: :separate and disables all color output.

=== Color-Off Effects (Separate-Line Mode)

When colors are disabled but ANSI is supported, Canon uses text effects to distinguish changes:

  • Strikethrough (\e[9m): Removed text

  • Underline (\e[4m): Added text

These provide visual distinction even without color support.

=== Environment Variables

In addition to NO_COLOR, Canon supports these environment variables:

# Disable colors globally (CANON_USE_COLOR)
export CANON_USE_COLOR=false

# Enable colors globally (CANON_USE_COLOR)
export CANON_USE_COLOR=true

# Format-specific color control
export CANON_XML_DIFF_USE_COLOR=false
export CANON_HTML_DIFF_USE_COLOR=false
export CANON_JSON_DIFF_USE_COLOR=false
export CANON_YAML_DIFF_USE_COLOR=false

=== Terminal Detection

Canon detects terminal capabilities through these environment variables:

COLORTERM

Set to 24bit, truecolor, or true indicates True Color support

TERM

Terminal type:

256-color support

xterm-256color, screen-256color

Direct color support

xterm-direct

No color support

dumb, emacs

Assume basic ANSI color support

Most other values

CI

CI environment detection (plus CI-specific variables)

If no terminal information is available, Canon assumes color support for modern terminals.

== Diff Type Display Options

Control which types of diffs to show using the show_diffs option:

Option What’s Shown

:all (default)

Both normative and informative diffs

:normative

Only normative diffs (red/green)

:informative

Only informative diffs (cyan)

# CLI - only show differences that affect equivalence
canon diff file1.xml file2.xml --show-diffs normative
# Ruby API
Canon.compare(file1, file2,
  format: :xml,
  show_diffs: :normative
)

== Visual Examples

=== Complete Diff with All Colors

Algorithm: SEMANTIC TREE DIFF        # Cyan + Bold

Visual Diff:                         # Cyan + Bold
root
├── ~ attributes                     # Yellow (normative)
│   ├── - id: "old"                 # Red (normative deletion)
│   └── + id: "new"                 # Green (normative addition)
├── ~~ whitespace                    # Cyan (informative)
│   └── ~~ extra spaces             # Cyan (informative)
└── + element                        # Green (normative addition)
    └── value: "new"

Line-by-line diff (XML mode):       # Cyan + Bold
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━    # Cyan

   1|  1 | <root>                    # Default (context)
   2|   -| <item░id="old"/>          # Red (normative deletion)
     |  2+| <item░id="new"/>          # Green (normative addition)
   3| ~~ | <p>Text</p>               # Cyan (informative)
     | ~~ | <p>░░Text</p>             # Cyan (extra spaces, informative)

== Colorblind-Friendly Features

While Canon uses colors, it also provides symbols that work without color:

  • Symbols (-, +, ~) distinguish change types

  • Line numbers show correspondence

  • Tree structure shows hierarchy

  • Text labels describe differences

The combination ensures diffs are readable even without color perception.

== Related Features

== See Also