Why step="1" Can Accept 1.25 but Reject 1
An enabled native number input has step="1", accepts 1.25, and rejects 1. Before replacing its validation, inspect its origin. With min="0.25", the permitted grid starts a quarter unit away from the familiar integer grid. The distance between neighboring grid points is one; the grid points themselves need not be integers. Here, “accepts” means that checkValidity() returns true, not that a keyboard prevents every invalid entry. The result is consistent with the documented step-base rule.
The more revealing failure needs no change to the current number. After the live value has been assigned, a script updates defaultValue; the live value remains 1.25, but native validity flips. A review that records only the live value and the step will miss the changed constraint.
This playbook follows fourteen controlled DOM checks: zero-origin baselines, a quarter-origin minimum, a value-attribute fallback, precedence controls, a deliberately weakened grid, and a three-stage failure-and-repair sequence. The supplied Chromium evidence preserves the current value across that final sequence.
The objective is not to make every row valid. It is to distinguish an expected native rejection from an unintended product-policy change. For the synthetic repair, the product explicitly authorizes both a quarter-offset grid and a minimum of 0.25.
That authorization matters. An explicit minimum is a sound repair only when its lower-bound effect is also intended. Otherwise, retain the evidence and hold the integration decision rather than silently changing accepted numbers.
Define the numeric grid and the permitted lower bound
Begin with a contract that a reviewer can evaluate independently of the browser. For the nonempty numeric candidates considered here, the synthetic product admits 0.25 + n, where n is a nonnegative integer. Its first values are 0.25, 1.25, 2.25, and 3.25. This is a stated laboratory policy, not a claim about what every number field should accept.
Separate its two conditions. Grid membership asks whether a candidate is a whole number of steps from the origin. The lower bound asks whether the candidate is at least 0.25. Neither question replaces the other.
For these deliberately small quarter-offset examples, the independent arithmetic is straightforward:
spacing s = 1
origin b = 0.25
on the grid: (x - b) / s is an integer
inside the approved lower bound: x >= 0.25
Thus 1.25 is one step from the origin. The integer 1 is only three quarters of a step away. Meanwhile, -0.75 is exactly one step below the origin: it belongs to the mathematical grid but violates the approved minimum. These calculations determine expectations before any DOM result is read.
The quarter offsets keep the investigation about origin selection, not a general-purpose decimal precision strategy. Do not turn this arithmetic into an unqualified JavaScript validator for arbitrary magnitudes or decimal strings. The fixture has a small, explicit input domain and literal expected outcomes.
Readers reviewing the underlying element model can use native HTML and CSS foundations as background. The work here is narrower: qualify the attributes that determine a native numeric constraint, without adding a page-building or styling exercise.
Keep diagnostic controls distinct from the approved product configuration. A zero-origin input is useful evidence, even though its accepted values differ from the synthetic product. A test can correctly characterize that control without approving it for this application.
Read the step base from the actual DOM attributes
For the ordinary type="number" controls in this fixture, the HTML Standard's step-base algorithm selects the base in this order: a min content attribute that converts successfully to a number; otherwise, a value content attribute that converts successfully; otherwise, zero. A present but unusable min does not win merely because the attribute exists. The relevant algorithm examines content attributes, not whichever number currently appears in .value.
Keep spacing and origin as separate entries in the diagnosis. Here, step="1" supplies the spacing. The selected base supplies the origin. Reading the first without establishing the second cannot explain an offset-grid result.
Inspect the actual element after the relevant application writes have occurred. A proposed source of the base is not evidence that the browser is using it. For example, a visible value="0.25" attribute cannot determine the outcome until the reviewer has also checked whether a valid min takes precedence.
Do not confuse the value attribute with the live value
Record getAttribute("value"), defaultValue, and .value separately. MDN documents defaultValue as reflecting the value content attribute. It is not another name for every update to the current value.
H7 deliberately supplies only step="1" and then assigns .value = "0.25". Its value attribute remains absent. By contrast, H5 supplies value="0.25" before assigning the live value "1.25". Those are different configurations, even though both operations involve something called “value.”
Preserve that distinction in logs. Use null for an absent attribute, and retain the live string separately from valueAsNumber. An empty reflected default and a missing content attribute should not be casually rewritten as the numeric origin zero in the evidence record.
The reviewer can then derive the origin from the captured attributes rather than asking a logger to guess it. That separation makes the report useful when the suspected defect is precisely an incorrect assumption about which value source controls validation.
Run the complete fourteen-case Chromium fixture
The supplied commissioning record reports PASS_14_CASES on CPython 3.13.5, Playwright 1.57.0, and Chromium 144.0.7559.96, on Linux. It started at 2026-10-14T08:26:40.337206+00:00 and finished at 2026-10-14T08:26:41.305832+00:00. These timestamps identify the run; they are not a performance benchmark.
The reference driver below is preserved from that evidence. Its UTF-8 source, with LF line endings and a final newline, has SHA-256 9b8f134b5d9edcdca9683c7316b75b4d8dfe4b28826890b86e8f9f1284092197. Any edited copy should be retained under its own digest and accompanied by its actual execution report, not associated automatically with the recorded result.
Use a writable disposable directory and save the complete code as number_step_fixture.py. In the intended Python environment, install the recorded Playwright package version:
python -m pip install "playwright==1.57.0"
The commissioning run selected an installed browser explicitly because the default Playwright-bundled executable was absent:
python number_step_fixture.py --executable /usr/bin/chromium
On another machine, supply its actual executable path. This option does not install Chromium or establish that an arbitrary browser build is compatible. Playwright documents browser-version dependencies and cautions about custom browser executables. Treat a different binary as a new environment to qualify.
A separate setup option is to install Playwright's corresponding Chromium binary and omit the executable argument:
python -m playwright install chromium
python number_step_fixture.py
That route may produce a different browser version from the commissioning run. Keep the version actually returned by the driver; do not copy the historical version string into a new report. Browser installation and system dependencies belong to setup, not to evidence that the fourteen cases passed.
Keep programmatic validity evidence in its proper scope
The browser creates one fresh page, inserts a small form container, and constructs ordinary enabled number inputs. No application server, external page, framework, or submitted request is involved. The first eleven cases use separate controls. H12 through H14 intentionally reuse one control so the before, failure, and repair states share the same live value history.
Each row records the live string, numeric conversion, value attribute, reflected default, min, step, the targeted validity flags, auxiliary flags, and checkValidity(). MDN describes stepMismatch as a validity-state Boolean and checkValidity() as the element's constraint-checking method. Neither name turns these programmatic observations into a user-interaction test.
"""Native number-input validity; no server, framework, reset, or submission."""
import argparse
import hashlib
import importlib.metadata
import json
import platform
import tempfile
from datetime import datetime, timezone
from pathlib import Path
from playwright.sync_api import sync_playwright
SCRIPT = r"""() => {
const rows = [];
const require = (ok, message) => { if (!ok) throw new Error(message); };
function make(attrs, live) {
const input = document.createElement('input');
input.type = 'number';
for (const [name, value] of Object.entries(attrs)) input.setAttribute(name, value);
document.querySelector('form').append(input);
input.value = live; // Sets the dirty value flag; does not set the value attribute.
return input;
}
function capture(id, input, expected, gridDecision) {
const row = {id, status: 'FAIL', live: input.value,
numeric: input.valueAsNumber, value_attribute: input.getAttribute('value'),
defaultValue: input.defaultValue, min: input.getAttribute('min'),
step: input.getAttribute('step'), stepMismatch: input.validity.stepMismatch,
rangeUnderflow: input.validity.rangeUnderflow, badInput: input.validity.badInput,
valueMissing: input.validity.valueMissing, checkValidity: input.checkValidity(),
expected, gridDecision};
rows.push(row);
require(input.type === 'number' && input.willValidate, id + ': wrong control state');
require(!row.badInput && !row.valueMissing, id + ': unrelated failure');
require(JSON.stringify([row.stepMismatch, row.rangeUnderflow, row.checkValidity]) ===
JSON.stringify(expected), id + ': validity manifest mismatch');
row.status = 'PASS';
return row;
}
try {
const cases = [
['H1_zero_base_integer', {step:'1'}, '1', [false,false,true], 'ZERO_GRID_CONTROL'],
['H2_zero_base_fraction', {step:'1'}, '1.25', [true,false,false], 'OFF_ZERO_GRID'],
['H3_min_base_fraction', {min:'0.25',step:'1'}, '1.25', [false,false,true], 'ON_OFFSET_GRID'],
['H4_min_base_integer', {min:'0.25',step:'1'}, '1', [true,false,false], 'OFF_OFFSET_GRID'],
['H5_value_attribute_base', {value:'0.25',step:'1'}, '1.25', [false,false,true], 'ON_OFFSET_GRID'],
['H6_value_base_integer', {value:'0.25',step:'1'}, '1', [true,false,false], 'OFF_OFFSET_GRID'],
['H7_live_value_not_base', {step:'1'}, '0.25', [true,false,false], 'OFF_ZERO_GRID'],
['H8_min_overrides_value', {min:'0',value:'0.25',step:'1'}, '1.25', [true,false,false], 'OFF_ZERO_GRID'],
['H9_invalid_min_fallback', {min:'invalid',value:'0.25',step:'1'}, '1.25', [false,false,true], 'FALLBACK_CONTROL'],
['H10_any_weakens_grid', {min:'0.25',step:'any'}, '1.5', [false,false,true], 'REJECT_FOR_OFFSET_GRID'],
['H11_grid_valid_range_invalid', {min:'0.25',step:'1'}, '-0.75', [false,true,false], 'RANGE_REJECT']
];
for (const [id,attrs,live,expected,decision] of cases) {
capture(id, make(attrs, live), expected, decision);
}
const input = make({value:'0.25',step:'1'}, '1.25');
capture('H12_before_default_change', input, [false,false,true], 'ON_OFFSET_GRID');
input.defaultValue = '0';
const changed = capture('H13_default_change_same_live', input, [true,false,false], 'REPAIR_BASE_POLICY');
require(changed.live === '1.25' && changed.value_attribute === '0', 'Dirty-value control failed');
input.min = '0.25'; // Approved minimum and offset in this synthetic application.
const fixed = capture('H14_explicit_approved_minimum', input, [false,false,true], 'ACCEPT_BOUNDED');
require(fixed.live === '1.25' && fixed.value_attribute === '0', 'Repair changed the live value');
require(rows.length === 14 && new Set(rows.map(r => r.id)).size === 14, 'Incomplete inventory');
return {status:'PASS_14_CASES', cases:rows};
} catch (error) {
return {status:'FAIL', cases:rows, error:{name:error.name, message:error.message}};
}
}"""
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--executable', help='Optional installed Chromium executable path')
args = parser.parse_args()
destination = Path(tempfile.mkdtemp(prefix='number-step-', dir=Path(__file__).parent))
report = {'revision':'number-step-v1', 'status':'FAIL', 'cases':[],
'started_utc':datetime.now(timezone.utc).isoformat(),
'python':platform.python_version(), 'platform':platform.platform(),
'playwright':importlib.metadata.version('playwright'),
'source_sha256':hashlib.sha256(Path(__file__).read_bytes()).hexdigest()}
try:
with sync_playwright() as p:
options = {'headless':True}
if args.executable:
options['executable_path'] = args.executable
browser = p.chromium.launch(**options)
try:
report['browser'] = browser.version
page = browser.new_page()
page.set_content('<!doctype html><html lang="en"><title>Step fixture</title><form></form></html>')
report.update(page.evaluate(SCRIPT))
if report['status'] != 'PASS_14_CASES':
raise RuntimeError('Browser fixture failed; inspect partial ledger')
finally:
browser.close()
except Exception as error:
report['status'] = 'FAIL'
report['driver_error'] = {'type':type(error).__name__, 'message':str(error)}
raise
finally:
report['finished_utc'] = datetime.now(timezone.utc).isoformat()
path = destination / 'results.json'
path.write_text(json.dumps(report, indent=2, allow_nan=False) + '\n')
print(path)
if name == '__main__':
main()
Read the output as an assertion ledger, not a list of fields that must all be valid. H2, for example, passes its test when the browser rejects the off-grid fraction. Its expected triple contains true for stepMismatch and false for checkValidity().
The assertions compare literal expected triples, require a number control that participates in validation, reject badInput and valueMissing, preserve the dynamic live-value invariants, and require fourteen unique case IDs. The driver records more state than it individually asserts: an integration review should also compare recorded attributes, defaults, and numeric values against its approved manifest.
For a useful rerun comparison, examine the report in three layers. First establish execution identity: source digest, Python version, Playwright version, browser version, and timestamps. Then reconcile the case inventory and literal expectations. Finally, compare the recorded DOM state that explains each result. Keeping those layers separate prevents a familiar status string from substituting for evidence about which program and browser produced it.
Do not erase a mismatch by replacing the reference expectations with the observed values. Retain the failing report and the changed source, when applicable. A browser launch failure, a missing case, and an unexpected validity triple are different findings; none should be relabeled as a successful run with an inconvenient detail omitted.
The browser-side error handler returns a partial ledger when an assertion throws. The Python wrapper marks a failed browser fixture as FAIL, includes driver error information, and attempts to write results.json in a uniquely created directory. Read the top-level status as well as the rows; an error after a row capture can invalidate the run even when earlier rows passed. These are properties visible in the supplied source, not separately fault-injected observations.
Missing dependencies, an unwritable directory, or a report-write failure cannot be treated as a completed qualification. Some setup operations precede the protected execution block, and no exhaustive storage-failure exercise is claimed. Require an actual complete report before using a new run as acceptance evidence.
This scope excludes human typing, mobile keyboards, other browser engines, transport behavior, and a complete accessibility review. A successful local suite establishes its named assertions in its recorded environment, not the quality of an entire user interface.
Compare the zero-origin and quarter-origin baselines
Read every result triple in the same order: (stepMismatch, rangeUnderflow, checkValidity). Reordering those fields in a discussion or spreadsheet creates an avoidable source of confusion.
H1 has no min or value content attribute. Its live string is "1", its numeric value is 1, and its expected triple is (false, false, true). H2 has the same attributes but uses live "1.25", numeric 1.25, and expects (true, false, false). These are the zero-fallback controls in the supplied manifest.
For an origin of zero, the arithmetic explains the pair: (1 - 0) / 1 = 1, while (1.25 - 0) / 1 = 1.25. Only the first quotient is an integer. There is no lower-bound rejection to explain the second result.
H3 and H4 both add min="0.25". H3 uses live "1.25", numeric 1.25, and expects (false, false, true). H4 uses live "1", numeric 1, and expects (true, false, false). The same spacing now accompanies a different origin.
The independent calculations are (1.25 - 0.25) / 1 = 1 and (1 - 0.25) / 1 = 0.75. Both candidates are above the minimum; their different outcomes concern grid membership, not range underflow.
This paired design gives the reviewer two comparisons. Within each pair, the attributes stay fixed while the candidate changes. Across the pairs, each candidate can be compared under the two origins. Neither comparison depends on a visual error message or a remembered template.
When investigating a real control, reproduce that discipline before editing it. Preserve the suspect candidate and change only the configuration under review. A test that changes the number, minimum, and step together may turn green without identifying which change mattered.
The qualified statement is therefore specific: under the quarter-origin policy used by H3 and H4, 1.25 is on-grid and 1 is off-grid. It is not a universal rule that fractions pass or integers fail when the step is one.
Let the value content attribute supply the fallback base
H5 removes the minimum and supplies value="0.25", with step="1" and live "1.25". Its expected triple is (false, false, true). H6 keeps those attributes but changes the live string to "1"; its expected triple is (true, false, false). This pair exercises the value-attribute base rather than a minimum-derived base.
The arithmetic is familiar, but the policy evidence is different. H5 and H6 obtain their quarter origin without declaring the approved lower bound. They are diagnostic controls, not interchangeable implementations of every condition in the bounded product contract.
H7 then removes the value attribute as well. The helper still assigns a current value, this time "0.25", but expects (true, false, false). The assignment does not provide the attribute-based origin present in H5. If the current candidate automatically supplied its own base, this negative control would lose its purpose.
Use these cases to investigate an existing component rather than to recommend mutable defaults as the universal home for business rules. The important review question is whether the application's chosen origin remains deliberate throughout the lifecycle being qualified.
Use the primary rule when shorthand becomes misleading
An explanation that says “step one means integers” needs its origin assumption. MDN's step documentation explicitly discusses a base supplied by min, then value, then zero. Read that rule alongside the WHATWG algorithm when a simplified example does not match the DOM under investigation.
Likewise, a worked example using a minimum is not evidence that a minimum is always present. Identify what the example assumes before extending its formula to H5 or H7.
For a defect report, retain the specific attribute state and case ID. “H7 has no value content attribute after a live-property assignment” is testable. “The input already has a value” leaves the decisive distinction unresolved.
Verify that min takes precedence and invalid min falls back
H8 deliberately makes the two candidate origins disagree. It sets min="0" and value="0.25", keeps step="1", and assigns live "1.25". Its expected triple is (true, false, false). This case supports a precedence claim that two equal candidate origins could not distinguish.
With origin zero, the candidate is off-grid. With origin 0.25, it would be on-grid. The selected literals therefore make the competing explanations produce different outcomes. That is why H8 is stronger than another example in which both attributes happen to contain the same number.
H9 changes the minimum to the literal string "invalid" while retaining value="0.25" and live "1.25". It expects (false, false, true): the unusable minimum does not supply the base, and the usable value attribute provides the fallback.
Do not reinterpret that result as a repaired minimum. MDN's minimum documentation explains that an unusable minimum leaves an input without that minimum when the input type supplies no default minimum. Passing H9 demonstrates fallback behavior; it does not establish an approved lower boundary.
An integration review should therefore distinguish browser behavior from configuration quality. The browser can follow its rules while the application has accidentally stopped expressing a product requirement. A successful fallback is not an authorization to keep malformed markup.
Retain "invalid" literally in the diagnostic record. Replacing it with null would erase the difference between an absent minimum and a present but unusable one. Normalizing both to 0 would introduce an origin that this case did not declare.
For remediation, trace where the malformed value was produced and obtain the intended minimum from the product contract. Do not choose a replacement merely because it produces the desired result for 1.25. The next candidate may expose the unreviewed boundary change.
Change defaultValue while preserving the live number
H12 through H14 form one stateful experiment. H12 begins with value="0.25", step="1", no minimum, and live "1.25". The helper has already assigned the live property. Its initial expected triple is (false, false, true).
H13 performs exactly this policy-relevant write:
input.defaultValue = '0';
The captured value attribute becomes "0"; the live string remains "1.25". The expected triple changes to (true, false, false). The driver explicitly checks both the unchanged live string and the changed attribute. The supplied commissioning record reports this transition as observed.
The documented mechanism has two parts. defaultValue reflects the content attribute. Separately, the HTML Standard's dirty-value handling distinguishes a live value already changed through .value from a clean control whose live value still follows content-attribute changes. The fixture relies on the former state. It does not read a public dirtyValueFlag property.
The arithmetic isolates the consequence: the candidate's distance from the origin changes from 1.25 - 0.25 = 1 to 1.25 - 0 = 1.25. The number is unchanged, but the grid against which it is evaluated is different.
Preserve the order of operations when reproducing the failure. The helper writes attributes first and then assigns the current value. H13 subsequently changes the reflected default. Omitting the live-property assignment would change the experiment's state precondition; it would no longer be the same controlled test of a fixed current number under a changed base.
This is the decisive reason to retain before-and-after attribute snapshots. A log containing only live="1.25" and step="1" would show no difference between the passing and failing states. It would omit the variable the experiment changed.
Keep this separate from saved-form restoration
The related discussion of saved defaults as a different form contract concerns which accepted state a form should later restore. That is a neighboring question, not the operation performed here.
This experiment never saves, restores, or contacts a server. The default matters solely because its reflected attribute participates in base selection. Investigate who wrote that attribute and why; do not import a persistence workflow to explain a validity change already accounted for by the captured DOM state.
Repair the origin only when the minimum is authorized
H14 changes the same element by assigning input.min = '0.25'. It does not restore the value attribute to "0.25"; that attribute remains "0". It does not rewrite the live number either. The final captured state retains live "1.25" and expects (false, false, true).
This repair is deliberately stronger evidence than changing several values at once. The now-valid minimum supplies the approved origin despite the remaining value attribute. Holding that attribute and the live value fixed makes the effect of the added minimum visible.
For this synthetic product, the configuration is authorized because the contract already requires 0.25 + n for nonnegative integers. The minimum encodes the intended boundary as well as anchoring the intended grid. Classify the result as ACCEPT_BOUNDED, not as a universal number-input repair.
Adding a minimum also excludes candidates below it. MDN documents that a value below the minimum fails the minimum constraint. This consequence must be reviewed independently of the improvement to step alignment.
Consider the mathematical counterexample already present in the fixture: -0.75 is on the quarter-offset grid. A product that permits negative steps from that origin would reject the proposed lower bound, even though H14's 1.25 becomes valid. The same configuration change can repair one intended contract and violate another.
The repair review should therefore preserve two explicit approvals: the origin is 0.25, and the lower bound is 0.25. Agreement on one does not imply agreement on the other. A ticket saying only “allow quarter-offset numbers” leaves the boundary decision open.
When that approval is missing, choose HOLD. Keep the current candidate unchanged while the owner specifies the permissible range and validation design. Do not substitute min="0", a convenient negative minimum, or an arbitrary value attribute simply to satisfy a single example.
Finally, bound the acceptance to the tested numeric cases and the stated policy. This fixture does not settle blank-field behavior, every admitted numeric spelling, arbitrary magnitudes, or the application's required browser coverage.
Reject step=any as a substitute for a discrete grid
H10 keeps min="0.25" but replaces the numeric step with step="any". It assigns live "1.5" and expects (false, false, true). The browser-valid result is the point of the negative control, not evidence that 1.5 satisfies the synthetic discrete-grid policy.
MDN documents any as removing the stepping restriction while leaving other constraints applicable. It is not a request to infer the intended interval from a default or to tolerate only a particular fractional offset.
The independent product check exposes the difference: (1.5 - 0.25) / 1 = 1.25, which is not an integer. The candidate is above the minimum but outside the approved grid. Consequently, the driver gives H10 the interpretation REJECT_FOR_OFFSET_GRID even though its native validity result is true.
That distinction is important when reviewing a proposed fix. “The error disappeared” is insufficient if the change removed the rule that was supposed to detect the error. Compare the accepted set against the product policy, not only the final Boolean for the original complaint.
Do not round the input to make the test green
Refonte's treatment of rounding to an increment as a separate operation addresses an explicitly different contract: producing an authorized rounded result. It is not the repair being qualified in this browser experiment.
Here, rewriting 1.5, truncating a fraction, or replacing 1.25 with a convenient integer would change the candidate under investigation. It would not explain which attribute-defined origin the browser used.
Keep value transformation out of this acceptance decision. Repair an unintended base when the intended constraints are known. Escalate an unknown business rule. Retain H10 so that a future change to any remains visible as a weakening of the discrete contract, rather than being celebrated as a general validation improvement.
A useful review question is whether the proposed patch preserves both approved acceptances and approved rejections. H10 exists specifically to prevent native validity alone from answering that broader question.
Keep step mismatch separate from range failure
H11 uses min="0.25", step="1", and live "-0.75". Its expected triple is (false, true, false). The value is aligned with the grid, below the minimum, and invalid overall.
The arithmetic separates the conditions cleanly. The difference from the base is -0.75 - 0.25 = -1, an integer multiple of the step. Meanwhile, -0.75 < 0.25 independently establishes the range violation. No rounding argument is needed to reconcile the two observations.
MDN documents rangeUnderflow as the flag for being below the permitted minimum. It answers a different question from stepMismatch. A clear step-mismatch flag therefore cannot stand in for a complete native-validity decision.
Likewise, checkValidity() === false tells the reviewer that an active constraint fails, but not which constraint explains the result. Retaining the individual flags prevents a range failure from being mislabeled as an origin defect.
No max, required, or custom validity message is active in these cases. The driver also requires badInput and valueMissing to remain false. These exclusions make H11 interpretable; they should not be mistaken for evidence about a component with additional active constraints.
Compare H11 with H4 before choosing a remedy. Both expect overall invalidity, but H4 has a step mismatch without range underflow, while H11 has range underflow without a step mismatch. A report that reduces both to “invalid number” discards the distinction needed to evaluate the proposed repair.
When integrating the check, preserve the failure category in the diagnostic record. A remediation for range underflow should not automatically modify the step. Conversely, an off-grid value above the minimum does not justify moving the lower boundary.
The practical acceptance question is conjunctive: does this candidate satisfy the approved grid and the approved range, along with every other active constraint? H11 demonstrates why a single favorable flag is not enough to answer it.
Reconcile the full validity matrix
The following fourteen-row manifest gives the literal expected outcomes and their interpretations. The supplied commissioning evidence reports that the fixture passed those expectations. The table states the expected contract; it does not derive that contract from a passing run. null means an absent content attribute; value and attribute cells preserve their string forms.
Case | Live value | Value attribute | min | step | stepMismatch | rangeUnderflow | checkValidity | Interpretation |
H1 | "1" | null | null | "1" | false | false | true | Zero-grid control |
H2 | "1.25" | null | null | "1" | true | false | false | Off zero grid |
H3 | "1.25" | null | "0.25" | "1" | false | false | true | On offset grid |
H4 | "1" | null | "0.25" | "1" | true | false | false | Off offset grid |
H5 | "1.25" | "0.25" | null | "1" | false | false | true | Value-attribute base |
H6 | "1" | "0.25" | null | "1" | true | false | false | Off value-based grid |
H7 | "0.25" | null | null | "1" | true | false | false | Live value supplies no base |
H8 | "1.25" | "0.25" | "0" | "1" | true | false | false | Valid min wins |
H9 | "1.25" | "0.25" | "invalid" | "1" | false | false | true | Invalid-min fallback |
H10 | "1.5" | null | "0.25" | "any" | false | false | true | Reject for product grid |
H11 | "-0.75" | null | "0.25" | "1" | false | true | false | Range rejection |
H12 | "1.25" | "0.25" | null | "1" | false | false | true | Before default change |
H13 | "1.25" | "0" | null | "1" | true | false | false | REPAIR_BASE_POLICY |
H14 | "1.25" | "0" | "0.25" | "1" | false | false | true | ACCEPT_BOUNDED |
The table abbreviates the full driver IDs to H1–H14; retain their full names in machine-readable reports. The compact table omits two fields retained by the driver: defaultValue and numeric, the latter being its name for valueAsNumber. For this fixture, the expected default is "" where the value attribute is absent and otherwise the attribute's string. Numeric values correspond to the displayed live strings, including 0.25, 1.5, and -0.75.
Keep the Boolean triples independent of observations. Do not generate an expected triple by reading the current validity flags and then compare the flags with that generated expectation. Such a comparison cannot detect disagreement with the approved manifest.
For a rerun, require the complete case inventory, the global passing status, and the preserved H12–H14 state relationship. A partial ledger or unexpected auxiliary flag is evidence to investigate, not a smaller substitute for the agreed fourteen-case qualification.
Audit the integration without importing a submission workflow
The local fixture isolates native semantics. An application review must next locate the writes that can produce its relevant states. Treat the following as a proposed integration review, not work already performed by the commissioning run.
Start with the generated markup, then inspect the actual DOM after initialization and after each relevant update. Record the input type, min, step, value content attribute, reflected default, live value, numeric value, and validity results at those boundaries. Identify which code owns each write rather than assigning ownership to whichever component happens to render the field.
Pay particular attention to updates described as default synchronization. H13 shows why the review cannot assume that an unchanged live number means an unchanged constraint policy. Compare the attribute snapshot before and after the update while preserving the candidate under investigation.
Keep submission entries versus native validity as a separate contract boundary. The neighboring article investigates entry completeness; this review does not construct an entry list or add a request handler.
Define accept, repair and hold decisions precisely
Use ACCEPT_BOUNDED when the origin and minimum are explicitly approved, the relevant manifest agrees with the recorded results, and the claim stays within the tested environment and numeric cases. Do not use it as shorthand for complete interface certification.
Use REPAIR_BASE_POLICY when an unintended attribute change moves the origin, as in H13. A change to step="any" also requires repair when the product still demands the discrete grid, even though native validity may be true.
Use HOLD when the product bounds, admitted input syntax, or required integration coverage remain unknown. Unknown browser requirements cannot be resolved by relabeling one Chromium run as cross-browser evidence.
Make the missing evidence concrete: the approved lower bound, the writer responsible for a changed default, or the named browser requirement awaiting execution. That gives the next owner a resolvable question instead of a vague instruction to “fix validation.”
Assign the constraint contract and its regression triggers
Assign policy and implementation decisions separately. The product owner approves the grid, bounds, and any additional rules outside this fixture. The component owner controls how those decisions become content attributes and live-property writes. QA owns the expected manifest, the execution record, and the stated limits of coverage.
For this case, a useful handoff includes the exact source, its digest, the report, the approved nonempty numeric policy, and the interpretation of each negative control. Keep a clear distinction between a recorded commissioning run and a later run with a changed driver or browser.
The broader practice of owning reproducible browser regression evidence provides useful organizational context. Apply it here to a specific decision: whether a change preserves the intended origin, spacing, and lower boundary. Do not replace that question with a count of automated tests.
Revalidate when min, step, value, or defaultValue changes. Also review changes to the input type, active constraints, rendering lifecycle, and actual browser-support requirements. Those are proposed regression triggers because they can alter the assumptions under which this manifest was interpreted.
Keep H8 and H9 together when reviewing origin selection: one distinguishes precedence, while the other distinguishes usable from unusable configuration. Keep H10 and H11 together when reviewing acceptance: one is browser-valid but product-invalid, while the other is grid-aligned but range-invalid. Keep H12 through H14 together when reviewing dynamic updates: separating them would discard the unchanged-value control.
For an unexpected result, first confirm the source digest and browser version, then inspect the captured DOM state. Decide whether the observation contradicts the expected native behavior or reveals that the application no longer expresses the approved policy. Those lead to different investigations.
Do not silently update the expected triples to match a changed result. Require an explanation and, when the product contract changes, an explicit revision of the policy and its acceptance evidence. Regression maintenance should preserve the reasoning that made the test valuable, not merely preserve a green status.
Develop stronger frontend reasoning through guided practice
A productive frontend exercise is to explain each result before running the fixture, then compare that explanation with the captured attributes and flags. The learning objective is not memorizing fourteen Booleans. It is recognizing which state determines the answer and which observation would disprove an assumption.
For guided foundations, explore Refonte Learning's Frontend Development program. Its published curriculum includes HTML5/CSS3, JavaScript, responsive design, React, and practical project work. Those foundations are relevant to reasoning about native controls; the program page does not establish that this exact laboratory or these pinned tool versions are taught.
Carry the same discipline into a component review: approve the grid and bounds, preserve the live candidate, and retain negative controls that distinguish competing explanations. Inspect the base and every active constraint before changing the value or loosening validation.
