Web UI: per-row unit suffixes, Fahrenheit support, dark-mode default - #18
Merged
Conversation
SebastianAment
force-pushed
the
web-ui-units-rework
branch
from
May 11, 2026 19:00
daad37b to
1de8757
Compare
…rk default - Extract UNITS and conversion helpers into docs/units.mjs as a pure module (single source of truth, importable by ui.mjs and Node-style tests). - Add per-row unit suffixes to the composition setter panel (kg/m³ ↔ lb/yd³ on mass rows, °C ↔ °F on the temperature row), right-aligned with the max-bound column on both desktop and mobile. - Convert temperature to Fahrenheit when imperial is active using the offset conversion F = C × 9/5 + 32. A single multiplicative factor was insufficient (this is why the temperature row was previously kept in °C regardless of unit system). Slider min/max bounds, click-to-edit commit, and unit-toggle snap all flow through offset-aware helpers. - Make dark mode the unconditional default. The inline theme bootstrap previously honored prefers-color-scheme: light, so visitors with a light-mode OS landed in light mode without pressing the toggle. The matchMedia listener that wiped a user's stored preference on OS appearance change is also removed. - Add test/test_js_units.mjs (67 assertions, Node-only, mirrors the test_js_gp.mjs pattern). Covers factor constants, the °F offset, round-trip identity for both column kinds under both unit systems, column-name dispatch, and label mapping. - Add test/e2e/unit-toggle.spec.ts (Playwright) verifying the metric default, toggle to imperial, offset-aware temperature display (22°C → 71.6°F, not 22°F), round-trip preservation, and the temperature info-row min/max bounds.
SebastianAment
force-pushed
the
web-ui-units-rework
branch
from
May 11, 2026 19:26
1de8757 to
e4ef98a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Reworks how the concrete strength explorer in
docs/handles units, gives the temperature row real Fahrenheit support, and pins dark mode as the unconditional default theme. Refactors all unit conversions into a pureunits.mjsmodule that is the single source of truth for both the UI and tests.Changes
Per-row unit suffixes in the composition setter panel. Each non–Material-Source row now shows its own unit (
kg/m³/lb/yd³for masses,°C/°Ffor temperature), kept right-aligned with the max-bound column on both desktop (.info-row { padding: 0 2px }) and mobile (padding: 0 7px 0 0). Previously the unit was only in the section title.Fahrenheit conversion for temperature. The imperial display now uses the offset conversion
F = C × 9/5 + 32. The previous implementation used a single multiplicative factor per quantity, which can't represent°C ↔ °Fcorrectly — that's why the temperature was previously left in°Cregardless of unit system. Slider min/max bounds, click-to-edit commit, blur-revert, and the unit-toggle snap all flow through the offset-aware helpers.Single source of truth for units (
docs/units.mjs). New pure module exporting:UNITSforstrength(psi ↔ MPa),mass(kg/m³ ↔ lb/yd³),gwp(kg CO₂/m³ ↔ lb CO₂/yd³),cost(temp(°C ↔ °F).compToDisplay(col, internal, unitSystem)/compFromDisplay(...)— column-aware (handles mass factor and temp offset).celsiusToDisplay/displayToCelsius(temperature-specific).sliderUnitLabel(col, unitSystem).This module is imported by both
docs/ui.mjsandtest/test_js_units.mjs(Node), so the conversions are tested without a browser.Dark mode is the unconditional default. The inline theme bootstrap in
index.htmlpreviously honoredprefers-color-scheme: light, so visitors with a light-mode OS landed in light mode without pressing the toggle. NowgetEffectiveTheme()returns'dark'unless the user has an explicit stored preference. ThematchMedia('prefers-color-scheme: dark')change-listener that used to wipe the user's stored preference on OS appearance change is also removed.End-to-end unit-conversion review
The native units in the data and model are:
kg/m³,psi,°C,kg CO₂/m³,$/m³(perboxcrete/units.py). All imperial conversions in the JS factor table verified against external constants:1 / 145.04PSI_TO_MPA = 0.0068951.68562.20462 / 1.30795 = 1.68561.68561 / 1.30795 ≈ 0.76461 yd³ = 0.7646 m³× 9/5 + 32(offset)0/100 °C → 32/212 °FTesting
test/test_js_units.mjs— 67 Node-only assertions covering factor constants, offsets, round-trip identity for both column kinds under both unit systems, column-name dispatch, and label mapping. Run withnode test/test_js_units.mjs.test/e2e/unit-toggle.spec.ts— Playwright e2e covering: metric default labels, toggle to imperial flips bothkg/m³ → lb/yd³and°C → °F, the offset-aware temperature value (e.g.22 °C → 71.6 °F, not22 °F), round-trip preservation, and the temperature info-row min/max bounds (e.g.-20 °C / 22 °C → -4 °F / 72 °F).pytest176 passed,test_js_units.mjs67/67,test_js_gp.mjs90/90 JS↔Python parity assertions still passing.Files
docs/index.html | 13 +++----
docs/style.css | 38 +++++++++++++
docs/ui.mjs | 83 +++++++++++++++++++----------
docs/units.mjs | (new, ~90 lines)
test/test_js_units.mjs | (new, ~130 lines)
test/e2e/unit-toggle.spec.ts | (new, ~113 lines)
Out of scope / future
docs/model/mix_analyses.json(LLM-generated mix descriptions) still mentionkg/m³andpsiliterally; they don't currently re-render on toggle. Could be regenerated to use both, or replaced at runtime — left alone here.TODOinupdate(); when wired up, themm ↔ inconversion (factor 25.4 perboxcrete/units.py) would just need to be added toUNITSsimilarly.