diff --git a/src/web_generator.py b/src/web_generator.py
index 5d1c1b9e..0a48c5bb 100644
--- a/src/web_generator.py
+++ b/src/web_generator.py
@@ -8,17 +8,80 @@ import tempfile
from pathlib import Path
-def get_numeral_color(number, config):
- """Get color for numeral based on configuration."""
- if not config.get('colored_numerals', False):
- return "#333"
-
+def get_colored_numeral_html(number, config):
+ """Generate HTML for numeral with appropriate coloring based on configuration."""
color_scheme = config.get('color_scheme', 'monochrome')
- if color_scheme == 'monochrome':
- return "#333"
+
+ # For web display, automatically use colored numerals for non-monochrome schemes
+ use_colored = config.get('colored_numerals', False) or color_scheme != 'monochrome'
+
+ if not use_colored or color_scheme == 'monochrome':
+ return str(number)
+
+ # Use the same colors as in the Typst template
+ place_value_colors = [
+ "#2E86AB", # ones - blue
+ "#A23B72", # tens - magenta
+ "#F18F01", # hundreds - orange
+ "#6A994E", # thousands - green
+ "#BC4B51", # ten-thousands - red
+ ]
+
+ if color_scheme == 'place-value':
+ # Color each digit by its place value (right-to-left: rightmost is ones)
+ digits = str(number)
+ colored_spans = []
+
+ for i, digit in enumerate(digits):
+ place_idx = len(digits) - 1 - i # rightmost digit is place 0 (ones)
+ color_idx = place_idx % len(place_value_colors)
+ color = place_value_colors[color_idx]
+ colored_spans.append(f'{digit}')
+
+ return ''.join(colored_spans)
+ elif color_scheme == 'heaven-earth':
+ # Use orange (heaven bead color)
+ return f'{number}'
+ elif color_scheme == 'alternating':
+ # For alternating, use blue for simplicity in web display
+ return f'{number}'
else:
- # For colored schemes, use a darker color for visibility
- return "#222"
+ return str(number)
+
+
+def get_numeral_color(number, config):
+ """Get single color for numeral (kept for backwards compatibility with tests)."""
+ color_scheme = config.get('color_scheme', 'monochrome')
+
+ # For web display, automatically use colored numerals for non-monochrome schemes
+ use_colored = config.get('colored_numerals', False) or color_scheme != 'monochrome'
+
+ if not use_colored or color_scheme == 'monochrome':
+ return "#333"
+
+ # Use the same colors as in the Typst template
+ place_value_colors = [
+ "#2E86AB", # ones - blue
+ "#A23B72", # tens - magenta
+ "#F18F01", # hundreds - orange
+ "#6A994E", # thousands - green
+ "#BC4B51", # ten-thousands - red
+ ]
+
+ if color_scheme == 'place-value':
+ # For single color (used by tests), return highest place value color
+ digits = str(number)
+ place_idx = len(digits) - 1 # Most significant digit place
+ color_idx = place_idx % len(place_value_colors)
+ return place_value_colors[color_idx]
+ elif color_scheme == 'heaven-earth':
+ # Use orange (heaven bead color)
+ return "#F18F01"
+ elif color_scheme == 'alternating':
+ # For alternating, use blue for simplicity in web display
+ return "#1E88E5"
+ else:
+ return "#333"
def generate_card_svgs(numbers, config):
@@ -80,7 +143,7 @@ def generate_web_flashcards(numbers, config, output_path):
cards_html = []
for i, number in enumerate(numbers):
svg_content = card_svgs.get(number, f'')
- numeral_color = get_numeral_color(number, config)
+ colored_numeral = get_colored_numeral_html(number, config)
card_html = f'''
'''
cards_html.append(card_html)
diff --git a/templates/flashcards.typ b/templates/flashcards.typ
index 83bcf2fd..21494be6 100644
--- a/templates/flashcards.typ
+++ b/templates/flashcards.typ
@@ -139,13 +139,13 @@
)
// Draw heaven bead
- // Position inactive earth bead gap from reckoning bar: 19px you measured
- // Convert to same gap for heaven: heaven-earth-gap - gap - bead-size/2
- #let earth-gap = 19pt // Exact same gap as earth beads
+ #let heaven-gap = 5pt // Gap between active/inactive beads or bar/inactive beads
#let heaven-y = if heaven-active == 1 {
- heaven-earth-gap - bead-size / 2 - 1pt // Active (center just above bar)
+ // Active heaven bead: positioned close to reckoning bar
+ heaven-earth-gap - bead-size / 2 - 1pt
} else {
- heaven-earth-gap - earth-gap - bead-size / 2 // Inactive (same gap as earth, measured from reckoning bar)
+ // Inactive heaven bead: positioned away from reckoning bar with gap
+ heaven-earth-gap - heaven-gap - bead-size / 2
}
#let bead-color = if heaven-active == 1 {
@@ -171,9 +171,17 @@
#for i in range(4) [
#let is-active = i < earth-active
#let earth-y = if is-active {
+ // Active beads: positioned close to reckoning bar, in sequence
heaven-earth-gap + bar-thickness + 1pt + bead-size / 2 + i * (bead-size + bead-spacing)
} else {
- total-height - (4 - i) * (bead-size + bead-spacing) - 5pt + bead-size / 2
+ // Inactive beads: positioned after the active beads + gap, or after reckoning bar + gap if no active beads
+ if earth-active > 0 {
+ // Position after the last active bead + gap
+ heaven-earth-gap + bar-thickness + 1pt + bead-size / 2 + earth-active * (bead-size + bead-spacing) + heaven-gap + (i - earth-active) * (bead-size + bead-spacing)
+ } else {
+ // No active beads: position after reckoning bar + gap
+ heaven-earth-gap + bar-thickness + heaven-gap + bead-size / 2 + i * (bead-size + bead-spacing)
+ }
}
#let earth-bead-color = if is-active {
diff --git a/tests/references/card_7_front.png b/tests/references/card_7_front.png
index 06d3eb1b..32694387 100644
Binary files a/tests/references/card_7_front.png and b/tests/references/card_7_front.png differ
diff --git a/tests/test_web_generation.py b/tests/test_web_generation.py
index 417fda94..83cd697b 100644
--- a/tests/test_web_generation.py
+++ b/tests/test_web_generation.py
@@ -29,13 +29,21 @@ class TestWebGeneration:
def test_get_numeral_color_place_value(self, sample_config):
"""Test numeral color for place-value scheme."""
config = {**sample_config, 'color_scheme': 'place-value', 'colored_numerals': True}
- color = get_numeral_color(42, config)
- assert color == "#222" # Darker color for visibility
- # Without colored numerals, should return default
- config['colored_numerals'] = False
+ # Test different place values
+ color = get_numeral_color(7, config) # ones place
+ assert color == "#2E86AB" # blue
+
+ color = get_numeral_color(42, config) # tens place
+ assert color == "#A23B72" # magenta
+
+ color = get_numeral_color(456, config) # hundreds place
+ assert color == "#F18F01" # orange
+
+ # For place-value scheme, colored numerals are automatically enabled
+ config['colored_numerals'] = False
color = get_numeral_color(42, config)
- assert color == "#333"
+ assert color == "#A23B72" # Still colored because place-value auto-enables coloring
@patch('generate.generate_cards_direct')
def test_generate_card_svgs_success(self, mock_generate_cards_direct, sample_config, temp_dir):