D3 stacked bar chart with linear x axis
Before dealing with your main question here, which concerns the SVG rectangles, here are some advices:


Use an ordinal scale for the colors: instead of manipulating document.styleSheets and using classes, as you're doing right now, just create a proper ordinal scale...

var colorScale = d3.scaleOrdinal(d3.schemeCategory10);


... and use it to fill your rectangles, with style("fill", ...) method.
Don't call your scales x and y. Use more descriptive names, like xScale and yScale. Doing that, you avoid confusion with the x and y methods which are very common in D3, like in line generators or area generators, for instance.
Use d3.max in your yScale domain:

.domain([0, d3.max(transformedData, function(d) {
    return d.cumT
})])

The default domain in a D3 linear scale is ([0, 1]). Therefore, you don't need to set it in your yScale.


Back to your main issue:

Using a stack generator is "messy", according to you. However, "messy" is quite opinion based: for instance, I don't see anything messy here (except for those classes, which you don't need if you follow my advice about the color scale).

But if you still feel that using a stack generator creates a messy code here is my solution, using your non-stacked data array. First, pass your data array directly to the &lt;g&gt; elements as the data:

var g = maing.selectAll('g')
    .data(transformedData)
    .enter()
    .append('g');


Then, create the lower and upper rectangles using a each:

g.each(function(d, i){
    //code here
});


Inside the each you need two selections, one for the upper rectangles and other for the lower rectangles:

//these are the upper rectangles
var upperRect = d3.select(this).append("rect")
    .attr("x", function() {
        return xScale(d.cumT - d.t)
    })
    .attr("y", yScale(0))
    .attr("width", function() {
        return xScale(d.t)
    })
    .attr("height", function() {
        return yScale(d.p / d.t)
    })
    .style("fill", function() {
        return colorScale(i)
    })
    .style("opacity", .8);

//and these are the lower rectangles
var lowerRect = d3.select(this).append("rect")
    .attr("x", function() {
        return xScale(d.cumT - d.t)
    })
    .attr("y", yScale(d.p / d.t))
    .attr("width", function() {
        return xScale(d.t)
    })
    .attr("height", function() {
        return yScale(d.h / d.t)
    })
    .style("fill", function() {
        return colorScale(i)
    })
    .style("opacity", .6);


And here is the demo with those modifications:



var body = d3.select('body');
var svg = body.append('svg').attr('height', 200).attr('width', 500);
var maing = svg.append('g');

var inputData = JSON.parse('{"a":[20,10],"b":[50,0],"c":[45,60],"d":[10,40]}')

// generate class
var colorScale = d3.scaleOrdinal(d3.schemeCategory10);

// convert to flattened array
var vals = Array("p", "h");
var cumT = +0
var transformedData = Object.keys(inputData).map(function(d, i) {
  let o = {};
  o.mp = d;
  inputData[d].forEach(function(d, i) {
    o[vals[i]] = d
  })
  o.t = o.p + o.h;
  return o;
}).sort(function(a, b) {
  return b.t - a.t
}).map(function(d, i) {
  let o = d;
  o.cumT = cumT += d.t
  return o;
});

var g = maing.selectAll('g')
  .data(transformedData)
  .enter()
  .append('g')

var xScale = d3.scaleLinear()
  .domain([0, d3.max(transformedData, function(d) {
    return d.cumT
  })])
  .range([0, 100]);
// TODO: use d3.max here
var yScale = d3.scaleLinear()
  .range([0, 140]) // some predefined height

g.each(function(d, i) {
  var upperRect = d3.select(this).append("rect")
    .attr("x", function() {
      return xScale(d.cumT - d.t)
    })
    .attr("y", yScale(0))
    .attr("width", function() {
      return xScale(d.t)
    })
    .attr("height", function() {
      return yScale(d.p / d.t)
    })
    .style("fill", function() {
      return colorScale(i)
    })
    .style("opacity", .8);

  var lowerRect = d3.select(this).append("rect")
    .attr("x", function() {
      return xScale(d.cumT - d.t)
    })
    .attr("y", yScale(d.p / d.t))
    .attr("width", function() {
      return xScale(d.t)
    })
    .attr("height", function() {
      return yScale(d.h / d.t)
    })
    .style("fill", function() {
      return colorScale(i)
    })
    .style("opacity", .6);
})
&lt;script src="https://d3js.org/d3.v4.min.js" charset="utf-8"&gt;&lt;/script&gt;




The colors are different from your snippet because, in your snippet, you're creating the array first and sorting later. But this is easy to change, if you want:



var body = d3.select('body');
var svg = body.append('svg').attr('height', 200).attr('width', 500);
var maing = svg.append('g');

var inputData = JSON.parse('{"a":[20,10],"b":[50,0],"c":[45,60],"d":[10,40]}');

// generate class
var colorScale = d3.scaleOrdinal(d3.schemeCategory10);

// convert to flattened array
var vals = Array("p", "h");
var cumT = +0
var transformedData = Object.keys(inputData).map(function(d, i) {
  let o = {};
  o.mp = d;
  inputData[d].forEach(function(d, i) {
    o[vals[i]] = d
  })
  o.t = o.p + o.h;
  return o;
}).sort(function(a, b) {
  return b.t - a.t
}).map(function(d, i) {
  let o = d;
  o.cumT = cumT += d.t
  return o;
});

colorScale.domain(Object.keys(inputData));

var g = maing.selectAll('g')
  .data(transformedData)
  .enter()
  .append('g')

var xScale = d3.scaleLinear()
  .domain([0, d3.max(transformedData, function(d) {
    return d.cumT
  })])
  .range([0, 100]);
// TODO: use d3.max here
var yScale = d3.scaleLinear()
  .range([0, 140]) // some predefined height

g.each(function(d, i) {
  var upperRect = d3.select(this).append("rect")
    .attr("x", function() {
      return xScale(d.cumT - d.t)
    })
    .attr("y", yScale(0))
    .attr("width", function() {
      return xScale(d.t)
    })
    .attr("height", function() {
      return yScale(d.p / d.t)
    })
    .style("fill", function(d) {
      return colorScale(d.mp)
    })
    .style("opacity", .8);

  var lowerRect = d3.select(this).append("rect")
    .attr("x", function() {
      return xScale(d.cumT - d.t)
    })
    .attr("y", yScale(d.p / d.t))
    .attr("width", function() {
      return xScale(d.t)
    })
    .attr("height", function() {
      return yScale(d.h / d.t)
    })
    .style("fill", function(d) {
      return colorScale(d.mp)
    })
    .style("opacity", .6);
})
&lt;script src="https://d3js.org/d3.v4.min.js"&gt;&lt;/script&gt;




Finally, have in mind that the stack generator, despite being complicated, is the best approach if you have several rectangles per column. In my solution, I have two selections inside the each... however, if you had 5 rectangles per column, we would have 5 selections inside the each, and so on, which is a lot of duplicated code.