Here's something you can add to your StyleDefinitions
CellDynamicExpression ->
Dynamic[
Block[{g, e = EvaluationCell[]},
g = StylesheetFunctions`enclosingGroup[e];
SetOptions[
e,
{
CellMargins -> StylesheetFunctions`getMarginsFromGroup[e],
CellDynamicExpression -> None
}
]
],
TrackedSymbols :> {},
UpdateInterval -> Infinity,
Initialization :>
If[! System`Private`HasDownCodeQ[StylesheetFunctions`enclosingGroup],
StylesheetFunctions`getMarginsFromGroup[cell_] :=
Replace[
{
If[# === None, None,
AbsoluteCurrentValue[#, CellMargins]
] &@StylesheetFunctions`enclosingGroup[cell][[1]],
AbsoluteCurrentValue[ParentNotebook[cell],
{StyleDefinitions, CurrentValue[cell, CellStyle][[1]], CellMargins}]
},
{
{
{{l1_?NumberQ, _}, {_, _}},
{{l2_?NumberQ, r_}, {t_, b_}}
} :>
{{l1 + l2, r}, {t, b}},
{_, else_} :> else
}
];
StylesheetFunctions`enclosingGroup[
cell_, groupingRules_ : {"SectionGrouping", "TitleGrouping"}] :=
Block[
{
nb = ParentNotebook[cell],
c,
grs,
i,
cgr
},
c = Cells[nb];
grs = CurrentValue[c, CellGroupingRules];
i = FirstPosition[List @@ c[[;; , 1]], cell[[1]]][[1]];
cgr = grs[[i]];
(* take until we hit one of the groupingRules *)
Reap[
Catch[
MapThread[
If[MemberQ[groupingRules, Flatten[{#2}][[1]]],
Sow[#];
Throw["Done!"],
Sow[#]
] &,
Reverse /@ {
c[[;; i]],
grs[[;; i]]
}
];
Sow[None]
]
][[2]] // Flatten // Reverse
]
]
]
It's based on a core function to determine the cell group for a cell + something to easily inherit CellMargins
.
StylesheetFunctions`getMarginsFromGroup[cell_] :=
Replace[
{
If[# === None, None,
AbsoluteCurrentValue[#, CellMargins]
] &@StylesheetFunctions`enclosingGroup[cell][[1]],
AbsoluteCurrentValue[ParentNotebook[cell],
{StyleDefinitions, CurrentValue[cell, CellStyle][[1]], CellMargins}]
},
{
{
{{l1_?NumberQ, _}, {_, _}},
{{l2_?NumberQ, r_}, {t_, b_}}
} :>
{{l1 + l2, r}, {t, b}},
{_, else_} :> else
}
];
StylesheetFunctions`enclosingGroup[
cell_, groupingRules_ : {"SectionGrouping", "TitleGrouping"}] :=
Block[
{
nb = ParentNotebook[cell],
c,
grs,
i,
cgr
},
c = Cells[nb];
grs = CurrentValue[c, CellGroupingRules];
i = FirstPosition[List @@ c[[;; , 1]], cell[[1]]][[1]];
cgr = grs[[i]];
(* take until we hit one of the groupingRules *)
Reap[
Catch[
MapThread[
If[MemberQ[groupingRules, Flatten[{#2}][[1]]],
Sow[#];
Throw["Done!"],
Sow[#]
] &,
Reverse /@ {
c[[;; i]],
grs[[;; i]]
}
];
Sow[None]
]
][[2]] // Flatten // Reverse
]
Or if you just want to directly get the cells to embed in your sheet
CellPrint[
Cell[
StyleData[#],
CellDynamicExpression ->
Dynamic[
Block[{g, e = EvaluationCell[]},
g = StylesheetFunctions`enclosingGroup[e];
SetOptions[
e,
{
CellMargins -> StylesheetFunctions`getMarginsFromGroup[e],
CellDynamicExpression -> None
}
]
],
TrackedSymbols :> {},
UpdateInterval -> Infinity,
Initialization :>
If[! System`Private`HasDownCodeQ[StylesheetFunctions`enclosingGroup],
StylesheetFunctions`getMarginsFromGroup[cell_] :=
Replace[
{
If[# === None, None,
AbsoluteCurrentValue[#, CellMargins]
] &@StylesheetFunctions`enclosingGroup[cell][[1]],
AbsoluteCurrentValue[ParentNotebook[cell],
{StyleDefinitions, CurrentValue[cell, CellStyle][[1]], CellMargins}]
},
{
{
{{l1_?NumberQ, _}, {_, _}},
{{l2_?NumberQ, r_}, {t_, b_}}
} :>
{{l1 + l2, r}, {t, b}},
{_, else_} :> else
}
];
StylesheetFunctions`enclosingGroup[
cell_, groupingRules_ : {"SectionGrouping", "TitleGrouping"}] :=
Block[
{
nb = ParentNotebook[cell],
c,
grs,
i,
cgr
},
c = Cells[nb];
grs = CurrentValue[c, CellGroupingRules];
i = FirstPosition[List @@ c[[;; , 1]], cell[[1]]][[1]];
cgr = grs[[i]];
(* take until we hit one of the groupingRules *)
Reap[
Catch[
MapThread[
If[MemberQ[groupingRules, Flatten[{#2}][[1]]],
Sow[#];
Throw["Done!"],
Sow[#]
] &,
Reverse /@ {
c[[;; i]],
grs[[;; i]]
}
];
Sow[None]
]
][[2]] // Flatten // Reverse
]
]
]
]
] & /@ {"Input", "Output", "Text"};
Note that I added the line
CellDynamicExpression -> None
to turn this off after it applies. That's just for performance as you edit. If you need to refresh the margins (say you changed some grouping around) you can do it like so:
FrontEndExecute@
Map[FrontEnd`SetOptions[#, CellDynamicExpression -> Inherited] &, Cells[]];
Of course, if you just want to style after you've built your NB you can do so faster/more easily than I did here by just scanning through the info in Developer`CellInformation[Cells[]]
.

EnclosingGroup
is exposed in any part of the FE that I've seen, but in terms of efficient implementation of this, I'd recommend getting the cell styles via something likeWith[{c=Cells[], this=EvaluationCell[]}, With[{s=CurrentValue[c, CellStyle]}, ...]]
since that's only 3 round-trips to the FE. It seems more wasteful since you always pull every cell, but theCellObject
has a small memory footprint and so at the end of the day it will probably be faster $\endgroup$PreviousCell[CellStyle -> "Subsection" | "Section"]
? $\endgroup$