I'll post the code below. Don't be pout off because there is a lot of it. See the next post - it is pretty simple to just drop this code into your sheet. The next post tells you the small bits that you need to customise. First the CSS, which is in two parts. The layout of the buttons in sheet I used for testing - you can dispense with these because it won't be necessary for you: div.buttons { display : grid ; grid-template-columns : 50px 50px 60px ; } Now the rolltemplate CSS, which is very necessary (this is Jakob's Better Rolltemplate from the wiki, and could be swapped to a different rolltemplate if you know what youre doing) /* Smaller margins - remove these if you want the huge default left margin */ .sheet-rolltemplate-custom { margin-left : -37px ; } .withoutavatars .sheet-rolltemplate-custom { margin-left : -7px ; } .sheet-rolltemplate-custom .sheet-container { border : 1px solid ; /* by default, the border is the same color as the header. You can change this here, e.g. to black */ border-color : var ( --header-bg-color ); } /* Header formatting - title and subtitle */ .sheet-rolltemplate-custom .sheet-header { background-color : var ( --header-bg-color ); /* change text-align to center to center the header text */ text-align : left ; color : var ( --header-text-color ); padding : 5px ; } .sheet-rolltemplate-custom .sheet-title { font-size : 1.1em ; } .sheet-rolltemplate-custom .sheet-subtitle { font-size : .9em ; } /* example colors */ .sheet-rolltemplate-custom .sheet-container { /* this is the default color */ --header-bg-color : rgba ( 112 , 32 , 130 , 1 ); --header-text-color : #FFF ; } .sheet-rolltemplate-custom .sheet-container.sheet-color-red { --header-bg-color : #F00 ; } .sheet-rolltemplate-custom .sheet-container.sheet-color-green { --header-bg-color : #0F0 ; --header-text-color : #000 ; } /* Allprops part */ .sheet-rolltemplate-custom .sheet-content { display : grid ; background : #FFF ; /* Header formatting - modify the column layout below */ grid-template-columns : auto auto ; /* Line height to match default roll template */ line-height : 1.4em ; } .sheet-rolltemplate-custom .sheet-content > div { padding : 5px ; } /* Left column */ .sheet-rolltemplate-custom .sheet-content .sheet-key { font-weight : bold ; padding-right : 10px ; text-align : right ; } /* Empty rule, use this if you want to change the right column .sheet-rolltemplate-custom .sheet-value { } */ /* Make even-numbered rows grey */ .sheet-rolltemplate-custom .sheet-content :nth-child ( 4n+3 ), .sheet-rolltemplate-custom .sheet-content :nth-child ( 4n ) { background : #EEE ; } /* Description field */ .sheet-rolltemplate-custom .sheet-desc { grid-column : span 2 ; padding : 5px ; text-align : center ; } Then on to the stuff that goes in the HTML page First the test buttons I created. Most of this you can delete, it was for my test: < div class = "buttons" > < span > Brace: </ span > < input type = "number" name = "attr_brace" value = "2" > < button type = "roll" name = "roll_brace" value = "@{brace_stat}" > Roll </ button > < input type = "hidden" name = "attr_brace_stat" value = "" > < button type = "action" name = "act_brace_button" class = "hidden" > Brace </ button > < span > Fist: </ span > < input type = "number" name = "attr_fist" value = "2" > < button type = "roll" name = "roll_fist" value = "@{fist_stat}" > Roll </ button > < input type = "hidden" name = "attr_fist_stat" value = "" > < button type = "action" name = "act_fist_button" class = "hidden" > Fist </ button > </ div > I'd recommend creating a blank sheet in the sandbox to test with this code so you can see how this part works. the first roll button in each group (roll_statname) is visible, and relies on the two hidden elements that follow (a hidden input and a hidden action button). These are used by the sheet worker below that makes everything work. Then the rolltemplate part, to layout the results (I always put rolltemplate code after the main html and just before the scipt block containing the sheet workers) < rolltemplate class = "sheet-rolltemplate-custom" > < div class= "sheet-container sheet-color- {{ color }} " > < div class= "sheet-header" > {{#title}} < div class= "sheet-title" > {{ title }} </ div > {{/title}} {{#subtitle}} < div class= "sheet-subtitle" > {{ subtitle }} </ div > {{/subtitle}} </ div > < div class= "sheet-content" > < div class= "sheet-key" > Roll </ div > < div class= "sheet-value" > {{ ability }}{{#rollGreater () advantage 0 }} **+** {{ advantage }}{{/rollGreater () advantage 0 }} **d6** </ div > {{#rollGreater () edge 0 }} < div class= "sheet-key" > Edge </ div > < div class= "sheet-value" > {{#rollTotal () edge 1 }} **Grace** {{/rollTotal () edge 1 }} {{#rollTotal () edge 2 }} **Iron** {{/rollTotal () edge 2 }} {{#rollTotal () edge 3 }} **Instinct** {{/rollTotal () edge 3 }} {{#rollTotal () edge 4 }} **Sharps** {{/rollTotal () edge 4 }} {{#rollTotal () edge 5 }} **Teeth** {{/rollTotal () edge 5 }} {{#rollTotal () edge 6 }} **Tides** {{/rollTotal () edge 6 }} {{#rollTotal () edge 7 }} **Veils** {{/rollTotal () edge 7 }} **(+1d6)** </ div > {{/rollGreater () edge 0 }} {{#rollGreater () cut 0 }} < div class= "sheet-key" > Cut </ div > < div class= "sheet-value" > **-** {{ cut }} </ div > {{/rollGreater () cut 0 }} < div class= "sheet-key" > Rolled </ div > < div class= "sheet-value" > {{ computed ::ability }} **=** {{ computed ::roll }} </ div > {{#rollGreater () computed::twist 0 }} < div class= "sheet-key" > Twist </ div > < div class= "sheet-value" ></ div > {{/rollGreater () computed::twist 0 }} {{#allprops () title subtitle desc color edge advantage cut roll twist ability }} < div class= "sheet-key" > {{ key }} </ div > < div class= "sheet-value" > {{ value }} </ div > {{/allprops () title subtitle desc color edge advantage cut roll twist ability }} {{#desc}} < div class= "sheet-desc" > {{ desc }} </ div > {{/desc}} </ div > </ div > </ rolltemplate > Most of the sheet worker is the aforementioned Jakob's Better Rolltemplate, with some tweaks to accept the sheet worker output. Finally the sheet worker where the magic happens, inside its own script block. There two sheet workers here. The first populates the hidden input for each stat. The second does the actual roll. There's exactly one thing you have to change in this code, and I'll explain that after: < script type = "text/worker" > /* this function uses a complex rolltemplate; every element can be displayed in the output several items have information in the .computed element: ability: the full set of starting dice are stored here cut: the dice after some are cute are stored here roll: the sum of the rolled and kept dice twist: whether there is a twist value. */ const roll_buttons = { brace_button: "brace" , fist_button: "fist" }; on ( "sheet:opened change:character_name" , function ( eventInfo ) { getAttrs ([ "character_name" , ... Object . keys ( roll_buttons )], function ( v ) { var attrsToChange = {}; Object . keys ( roll_buttons ). forEach ( button => { const stat = roll_buttons [ button ]; attrsToChange [ stat + "_stat" ] = "%{" + v [ "character_name" ] + "|" + stat + "_button}" ; }); setAttrs ( attrsToChange ); }); }); Object . keys ( roll_buttons ). forEach ( function ( button ) { on ( `clicked: ${ button } ` , () => { let stat = roll_buttons [ button ]; let rolltext = `&{template:custom} {{title=@{character_name} makes a ${ stat [ 0 ]. toUpperCase ()+ stat . substring ( 1 ) } roll}} {{edge=[[?{Edge?|None,0|Grace,1|Iron,2|Instinct,3|Sharps,4|Teeth,5|Tides,6|Veils,7}]]}} {{ability=[[@{ ${ stat } }]]}} {{advantage=[[?{Advantage? (up to 2d6)|0|1|2} ]]}} {{cut=[[?{Cut?|0}]]}} {{roll=[[ @{ ${ stat } }d6 +[[{?{Edge?},1}dh1]]d6 + ?{Advantage? (up to 2d6)}d6 ]]}} {{twist=[[0]]}}` ; startRoll ( rolltext , ( output ) => { let dice_all = output . results . roll . rolls . reduce (( all , one ) => [... all , ... one . results ],[]). sort (); let cut = output . results . cut . result ; dice_all . length = Math . max ( 0 , dice_all . length - cut ); let uniqueDice = [... new Set ( dice_all )] let twist = ( dice_all . length === uniqueDice . length ) ? 0 : 1 ; let dice_ability = output . results . roll . dice . sort (); let score = dice_all [ dice_all . length - 1 ] || 0 ; let rollData = {}; rollData . ability = dice_ability , rollData . cut = dice_all , rollData . roll = score , rollData . twist = twist finishRoll ( output . rollId , rollData ); }); }); }); </ script > The only part of this that needs changing is this section: const roll_buttons = { brace_button: "brace" , fist_button: "fist" }; You need to add a line for each stat, with the name of the button (fist_button:) followed by the name fothe attribute). So most of this code can be used as is, I'll make another post describing exactly what you need to change for simplicity.