I've been using several different conditional formats applied to the entire column, or group of columns. The table looks like this:

The following formulas are conditional formatting applied to the entire row/rows, not individual cells:
Header:
=AND(OR(ISERR(OFFSET(A1,-1,0)),ISBLANK(OFFSET(A1,-1,0)))=TRUE,ISBLANK(A1)=FALSE)
Band:
=AND(CELL("row",A1)=EVEN(CELL("row",A1)),ISBLANK(A1)=FALSE)
Last row (or total row):
=AND(ISBLANK(A1)=FALSE,ISBLANK(A2)=TRUE)
With the following formatting:

If you do not want this formatting to be applied to non-spill cells, you can determine if a cell was spilled or directly input. You can do this with a formula or write a UDF.
Using a formula you need to fake it. You can use ISFORMULA to find where the spill formula was entered, and NOT(ISBLANK()) to identify spilled rows. You would then have to assume a formula followed by non-blank, non-formula cells is a spilled formula. Helper columns may be helpful.
Using a UDF, you can directly determine if a cell is spilled. Below is a basic example. You can add more checking to determine if the formula is actually spilled if desired.
Public Function isFormulaOrSpill(ByVal rRange As Range) As Boolean
Dim this_bIsSpill As Boolean
Dim this_bIsFormula As Boolean
this_bIsSpill = rRange.HasSpill
this_bIsFormula = rRange.HasFormula
isFormulaOrSpill = (this_bIsSpill Or this_bIsFormula)
End Function
Recently I've been building entire tables as spilled ranges (including header and total rows). Here is an example of those who want to give it a shot:
=LET(
Column_Key, Table_Status[System],
Column_FtEstimated, Table_Status[Estimated],
Column_FtModeled, IF(Table_Status[Modeled]>Table_Status[Estimated],Table_Status[Estimated],Table_Status[Modeled]),
Categories, SORT(UNIQUE(Column_Key)),
Array_BoolKey, (TRANSPOSE(Column_Key)=Categories)+0,
Mask1, TRANSPOSE(ISNUMBER(XMATCH(Column_Filter1,List_Filter1))),
Mask2, TRANSPOSE(ISNUMBER(XMATCH(Column_Filter2,List_Filter2))),
Array_BoolMasked, Array_BoolKey,
Masked_FtModeled, IFERROR(Array_BoolMasked*TRANSPOSE(Column_FtModeled),0),
Masked_FtEstimated, IFERROR(Array_BoolMasked*TRANSPOSE(Column_FtEstimated),0),
Array_Ones, SEQUENCE(COLUMNS(Array_BoolMasked),1,1,0),
Body_Count_Lines, MMULT(Array_BoolKey, Array_Ones),
Body_Sum_FtModeled, MMULT(Masked_FtModeled, Array_Ones),
Body_Sum_FtEstimated, MMULT(Masked_FtEstimated, Array_Ones),
Body_Percent_FtModeled, IFERROR(Body_Sum_FtModeled/Body_Sum_FtEstimated,"-"),
Total_Count_Lines, IFERROR(SUM(Body_Count_Lines),"-"),
Total_Sum_FtModeled, IFERROR(SUM(Body_Sum_FtModeled),"-"),
Total_Sum_FtEstimated, IFERROR(SUM(Body_Sum_FtEstimated),"-"),
Total_Percent_FtModeled, IFERROR(Total_Sum_FtModeled/Total_Sum_FtEstimated,"-"),
Array_Seq, {1,2,3,4,5},
Array_Header, CHOOSE( Array_Seq, "System", "Lines", "Modeled Feet", "Estimated Feet", "Percent Modeled"),
Array_Body, CHOOSE( Array_Seq, Categories, Body_Count_Lines, Body_Sum_FtModeled, Body_Sum_FtEstimated, Body_Percent_FtModeled),
Array_Total, CHOOSE( Array_Seq, "Total", Total_Count_Lines, Total_Sum_FtModeled, Total_Sum_FtEstimated, Total_Percent_FtModeled),
Range1,Array_Header,
Range2,Array_Body,
Range3,Array_Total,
Rows1,ROWS(Range1), Rows2,ROWS(Range2), Rows3,ROWS(Range3), Cols1,COLUMNS(Range1),
RowIndex, SEQUENCE(Rows1 + Rows2 + Rows3), ColIndex,SEQUENCE(1, Cols1),
RangeTable,IF(
RowIndex<=Rows1,
INDEX(Range1,RowIndex,ColIndex),
IF(RowIndex<=Rows1+Rows2,
INDEX(Range2,RowIndex-Rows1,ColIndex),
INDEX(Range3,RowIndex-Rows1-Rows2,ColIndex)
)),
Return, RangeTable,
Return
)
=E4#in the "Applies To" range, but it'll change to the hard-coded equivalent, e.g.$E$4:$E$7. Not sure this is a feature yet.applies to:. TheApplies to:will change it to an actual range no matter how it is entered. And that range will remain fixed regardless if the table increases or decreases in size.=INDIRECT("Table1[@col1]")..., it seemed to work dynamically regardless of the change of table size.