diff --git a/.jules/palette.md b/.jules/palette.md index 74cddb7ad..343d46755 100644 --- a/.jules/palette.md +++ b/.jules/palette.md @@ -19,3 +19,7 @@ ## 2024-07-13 - CLI Debugging Stack Traces **Learning:** Adding a `FAST_MLSIRM_DEBUG` bypass to user-friendly `try/except` blocks is crucial for DX. Otherwise, unexpected runtime errors during development will be swallowed into generic stderr messages, hiding the stack trace needed to actually fix the bug. **Action:** When adding `try-except` blocks to Python CLI subcommands to improve Developer Experience (DX) by preventing raw tracebacks for users, include a debug bypass (e.g., `if os.environ.get("FAST_MLSIRM_DEBUG"): raise`) in *all* catch blocks (including `RuntimeError` and `Exception`) to ensure tracebacks aren't swallowed during local development and debugging. + +## 2024-07-14 - Skip-Link Target Keyboard Focus Accessibility +**Learning:** Setting `outline: none;` on a skip-link target like `
` correctly hides the focus ring when activated via mouse click. However, it also completely removes the visible focus indicator when the skip-link is activated via keyboard navigation, leaving keyboard users without visual context of their position. +**Action:** Always complement `outline: none;` (or `:focus { outline: none; }`) with `:focus-visible { outline: 3px solid [color]; outline-offset: 3px; }` for skip-link targets and similarly keyboard-navigable containers to ensure accessibility. diff --git a/crates/fast-mlsirm-py/Cargo.lock b/crates/fast-mlsirm-py/Cargo.lock index 958118221..ef26f71e6 100644 --- a/crates/fast-mlsirm-py/Cargo.lock +++ b/crates/fast-mlsirm-py/Cargo.lock @@ -652,9 +652,9 @@ checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" [[package]] name = "pollster" -version = "0.4.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" +checksum = "bc6355899e1c9462875b6757c79f3caa011a1fdae12bbb1a2e72dd1f234f8336" [[package]] name = "portable-atomic" diff --git a/python/fast_mlsirm/report.py b/python/fast_mlsirm/report.py index 30ee4276b..6f41764e3 100644 --- a/python/fast_mlsirm/report.py +++ b/python/fast_mlsirm/report.py @@ -498,6 +498,11 @@ def _css() -> str: outline: none; } +main:focus-visible { + outline: 3px solid #0f766e; + outline-offset: 3px; +} + .hero { min-height: 172px; display: flex; diff --git a/tests/test_report.py b/tests/test_report.py index 52d1d9091..260a0097c 100644 --- a/tests/test_report.py +++ b/tests/test_report.py @@ -11,8 +11,16 @@ def test_render_fit_diagnostics_report_has_sections(tmp_path): source.write_text( json.dumps( { - "itemfit": {"item_id": [0, 1], "outfit_mnsq": [1.0, 1.2], "observed_count": [4, 4]}, - "personfit": {"person_id": [0, 1], "outfit_mnsq": [0.9, 1.1], "observed_count": [2, 2]}, + "itemfit": { + "item_id": [0, 1], + "outfit_mnsq": [1.0, 1.2], + "observed_count": [4, 4], + }, + "personfit": { + "person_id": [0, 1], + "outfit_mnsq": [0.9, 1.1], + "observed_count": [2, 2], + }, "factorfit": {}, "categoryfit": {}, "groupfit": {}, @@ -102,7 +110,11 @@ def test_render_report_summarizes_empty_metric_sections(tmp_path): json.dumps( { "model_fit": {}, - "itemfit": {"item_id": [0], "outfit_mnsq": [1.0], "observed_count": [4]}, + "itemfit": { + "item_id": [0], + "outfit_mnsq": [1.0], + "observed_count": [4], + }, } ), encoding="utf-8", @@ -178,7 +190,11 @@ def test_render_table_section_omits_empty_chart_placeholder(tmp_path): json.dumps( { "model_fit": {"loglik": -3.2}, - "itemfit": {"item_id": ["A"], "outfit_mnsq": [None], "observed_count": [4]}, + "itemfit": { + "item_id": ["A"], + "outfit_mnsq": [None], + "observed_count": [4], + }, } ), encoding="utf-8", @@ -201,7 +217,11 @@ def test_render_table_section_charts_later_numeric_rows(tmp_path): json.dumps( { "model_fit": {"loglik": -3.2}, - "itemfit": {"item_id": item_ids, "outfit_mnsq": outfit, "observed_count": [4] * 13}, + "itemfit": { + "item_id": item_ids, + "outfit_mnsq": outfit, + "observed_count": [4] * 13, + }, } ), encoding="utf-8", @@ -253,6 +273,7 @@ def test_render_table_region_has_keyboard_focus_style(tmp_path): assert 'tabindex="0"' in html assert ".table-wrap:focus-visible" in html assert ".table-wrap:focus {" not in html + assert "main:focus-visible {" in html assert "tbody tr:hover" in html assert '