@e4d The way it is working is as intended (at least it is how it is coded and documented). So I'm afraid that your understanding does not match the current implementation (though arguably the way you expect it to work would perhaps be better).
From the docs:
The requested PWM value (S parameter) is scaled to be between 0 and X parameter value, and rounded up to the minimum
and from the code:
reqVal = (val <= 0.0) ? 0.0 : max<float>(val * maxVal, minVal); // scale the requested PWM by the maximum, enforce the minimum
https://github.com/Duet3D/DuetWebControl/blob/master/src/components/panels/FanPanel.vue#L64
So basically it takes the requested value (0.0...1.0) and scales that by the maximum set value, it then compares that with the minimum set value and uses the larger of the two.
I'm not totally sure why the current implementation is the way it is, there may be a good reason. Perhaps @dc42 would like to comment.