Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

Functions & other useful tips from a pro

1585675222

Edited 1585727762
Cassie
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Just thought I'd share a bit of my setup for sheet programming with everyone. Install Pull to GitHub EVERYONE should install Pull on theirs forks!!&nbsp; <a href="https://wei.github.io/pull/#basic-setup" rel="nofollow">https://wei.github.io/pull/#basic-setup</a> &nbsp; Gigs, I think, found this a bit ago and it is incredibly useful for keeping forks updated. So many pull request get rejected or delayed due to a failure to keep GitHub forks in sync with the master repo. This will automate that for you. Use Branches in GitHub Do not make changes to your GitHub master. Instead make a branch from master to do all upgrades there. Ssubmit pull request to the Roll20 repo directly from a branch. This is a safe way to do version control and will improve the integrity of your work. PUG, SCSS, &amp; Javascript! Use PUG &amp; SCSS &nbsp;&amp; JavaScript to make sheets. PUG &amp; SCSS are the two most useful things I've ever learned for sheet development. Character sheets which use to take hundred+ hours takes now 20-40 thanks to PUG which is a Javascript based language to write HTML. You'll save yourself a lot of redundant work by just using the power of loops &amp; variables if nothing else. Troubleshooting becomes much easier when you need to fix just one line in a loop rather than a dozen copy/paste html snippits. These are compiled languages so you'll need to do that. Easily done via terminal if your tech savy or&nbsp; <a href="https://prepros.io/" rel="nofollow">https://prepros.io/</a> &nbsp;if you're not. Learn Grid CSS Grid is incredible. Learn it and use it,&nbsp; <a href="https://css-tricks.com/snippets/css/complete-guide-grid/" rel="nofollow">https://css-tricks.com/snippets/css/complete-guide-grid/</a> &nbsp; I didn't actually test this but it should work. ;) &lt;div class='grid 2autocolumn'&gt; &nbsp;&nbsp;&nbsp;&nbsp;&lt;span&gt;Character Name&lt;/span&gt; &nbsp;&nbsp;&nbsp;&nbsp;&lt;label title='character name'&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;input name='attr_character_name' type='text' placeholder='character name' value='' /&gt; &nbsp;&nbsp;&nbsp;&nbsp;&lt;/label&gt; &lt;/div&gt; With the html above you can get a nice header / input row with the following simple code. .sheet-grid { &nbsp;&nbsp;&nbsp;&nbsp;display: inline-grid; &nbsp;&nbsp;&nbsp;&nbsp;grid-column-gap: 1%; &nbsp;&nbsp;&nbsp;&nbsp;grid-row-gap: 0.5%; } label, input[type='text'] { &nbsp;&nbsp;&nbsp;&nbsp;width: 100% } div.sheet-2autocolumn { &nbsp;&nbsp;&nbsp;&nbsp;grid-template-columns: auto 1fr; } Pattern Libraries &amp; Components A pattern library can be your best friend if you want to make multiple sheets. Component style programming will let you save styles to be reused between sheets. With my last project I put work into my Sheet Template . This saves me a good deal of time by not needing to start from scratch every time. Its a slow work in progress were I'm reinventing much of Bootstrap . Helpful Functions //Convert Integers to be Negative const convertIntegerNegative = number =&gt; number &gt; 0 ? -Math.abs(number) : number //Convert an object with negative numbers const convertIntegersNegatives = numbers =&gt; { &nbsp; numbers =&gt; { &nbsp; &nbsp; for (let [key, value] of Object.entries(numbers)) { &nbsp; &nbsp; &nbsp; numbers[key] = convertIntegerNegative(value); &nbsp; &nbsp; } &nbsp; &nbsp; return numbers &nbsp; } } //Pass in eventinfo.triggerName const findRepeatingField = trigger =&gt; trigger.split('_')[1] //Pass in eventinfo.triggerName const getReprowid = trigger =&gt; { &nbsp; const split = trigger.split('_'); &nbsp; return `${split[0]}_${split[1]}_${split[2]}` } //Pass in an object keep that has the repeating section //Example repeating_weapon_-m1czg68yzicwhfdpyys_name const getReprowAttribute = key =&gt; { &nbsp; const getReprowid = processingFunctions.getReprowid(key) &nbsp; return key.split(`${getReprowid}_`)[1] } //Provide the function with an array of keys to find transations for&nbsp; //Example ['strenght', 'agility', 'willpower'] const getTranslations = translationKeys =&gt; { &nbsp; let translations = {} &nbsp; translationKeys.forEach(key =&gt; translations[`${key}`] = getTranslationByKey(key)) &nbsp; return translations } const parseInteger = string =&gt; parseInt(string) || 0 //Use for convernting the result of getAttrs from strings into integers const parseIntegers = numbers =&gt; { &nbsp; for (let [key, value] of Object.entries(numbers)) { &nbsp; &nbsp; &nbsp; numbers[key] = parseInt(value) || 0 &nbsp; } &nbsp; return numbers&nbsp;&nbsp; } const setAttributes = (update, silent) =&gt; silent &amp;&amp; typeof update === 'object' ? setAttrs(update, {silent:true}) : typeof update === 'object' ? setAttrs(update) : console.error(`${update} is not an object`) //returns strength from @{strenght} const sliceAttr = attribute =&gt; attribute.slice(2, -1) const sumIntegers = numbers =&gt; numbers.reduce((a,b) =&gt; a + b, 0) Edit 1: Added GitHub branches
1585676484
Kavini
Pro
Marketplace Creator
Sheet Author
Compendium Curator
This is a great guide, Cassie!
1585680157
Cassie
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Nic B.&nbsp; You're better at this than I am. What are you tips?
1585688748

Edited 1585689013
Andreas J.
Forum Champion
Sheet Author
Translator
Install Pull to GitHub I have to advice caution when setting it up, and to not set it to auto-update. It's highly likely that three sheet authors who had the bot set to auto-update had their PRs wiped by the bot forcing an update on the relevant branch in the short window of time that Roll20 took to start merging the weekly PRs. If you use it, don't have it at auto-update, but rather manually trigger it each Tuesday evening when it's certain things are done for the week. Otherwise this is great advice, some of which I haven't applied myself on my workflow :)
1585689119
vÍnce
Pro
Sheet Author
Andreas J. said: Install Pull to GitHub I have to advice caution when setting it up, and to not set it to auto-update. It's highly likely that three sheet authors who had the bot set to auto-update had their PRs wiped by the bot forcing an update on the relevant branch in the short window of time that Roll20 took to start merging the weekly PRs. If you use it, don't have it at auto-update, but rather manually trigger it each Tuesday evening when it's certain things are done for the week. You can manually trigger the Pull app anytime, but I'm not sure if there's a way you can configure the frequency. ;-( Pull app will automatically watch and pull in upstream’s default (master) branch to yours using hard reset every couple of hours. You can also manually trigger it anytime.
Regarding PUG and SCSS, will we still want to submit the PR with just the compiled HTML and CSS files?
1585689446
vÍnce
Pro
Sheet Author
Great post Cassie.&nbsp; Thank you
1585689788

Edited 1585689930
Andreas J.
Forum Champion
Sheet Author
Translator
Primal Zed said: Regarding PUG and SCSS, will we still want to submit the PR with just the compiled HTML and CSS files? No, obviously you'd want to keep the pug/scss in the repo as well, otherwise you'd make things harder than needed. Why would you have the pug/scss anywhere else? If you had, then nobody could collaborate on the full code for the sheet, defeating half the point with having a shared, open repo. Go check any of the sheets in the rpo that used pug/scss, and you'll know what I mean.
Go check any of the sheets in the rpo that used pug/scss, and you'll know what I mean. You're right, I should have checked for that to begin with. I'm used to SCSS, but Pug is new to me. It should be very helpful - I've been wanting a means of injecting templates / components while working on my changes. Thanks for the tip!
1585696146

Edited 1585696339
Cassie
Forum Champion
Sheet Author
API Scripter
Compendium Curator
You should set Pull to automatically upgrade and use branches to submit PRs to the repository. My best guess is those people who had their PRs wiped out were making changes to their master which is a bad practice. Learn to branch and you'll be happier for it.&nbsp; Primal Zed&nbsp; Out of a bit of laziness I do just leave the pug &amp; scss main files in the top folder. They would be better stored in a src file out of the way buuutttt.....
1585696461
Andreas J.
Forum Champion
Sheet Author
Translator
Cassie said: My best guess is those people who had their PRs wiped out were making changes to their master which is a bad practice. Learn to branch and you'll be happier for it.&nbsp; Oh dear, I went back and checked their submissions, and they all where using their master branch.
1585706932
GiGs
Pro
Sheet Author
API Scripter
This is great advice, but I'm in disagreement with the advice to use PUG and SCSS. This kind of approach has rendered at least one sheet on repo un-updatable, because the person originally building the sheet used this approach, and then abandoned the project. Now no-one who is interested in the system can grasp how the sheet works and its impossible to update. If you are updating a sheet it needs to be accessible to the community, most of which are not great coders. The reason the roll20 character sheet feature has been so successful, is because it is accessible to people who aren't professional coders. If we required PUG and SCSS from the start, then it would never have got off the ground. Okay you say, I can make the sheet with PUG and SCSS and publish the final version, so it is accessible. This might work! But I do see potential issues, when someone takes the accessible version and applies updates, and then you update with PUG/SCSS and replace their updates.&nbsp; I think they are great tools, and in a software environment where everyone is using them, thats great. But in an amateur environment like roll20, I honestly think the roll20 devs should consider banning them from use.&nbsp;
1585717537

Edited 1585717574
Is there a sample sheet somewhere in PUG and SCSS? I work better from examples and would like to see a version made with these tools and then see the final rendered version to see what it can do. Even a minimalist sheet would help me. GiGs, are you saying the rendered HTML and CSS are not readable like "manual" HTML? Would it help prevent problems if the uncompiled PUG and SCSS code was shared in the repo in addition to the rendered?
1585717761
MadCoder
Pro
Sheet Author
GiGs said: This is great advice, but I'm in disagreement with the advice to use PUG and SCSS. This kind of approach has rendered at least one sheet on repo un-updatable, because the person originally building the sheet used this approach, and then abandoned the project. Now no-one who is interested in the system can grasp how the sheet works and its impossible to update. If you are updating a sheet it needs to be accessible to the community, most of which are not great coders. The reason the roll20 character sheet feature has been so successful, is because it is accessible to people who aren't professional coders. If we required PUG and SCSS from the start, then it would never have got off the ground. Okay you say, I can make the sheet with PUG and SCSS and publish the final version, so it is accessible. This might work! But I do see potential issues, when someone takes the accessible version and applies updates, and then you update with PUG/SCSS and replace their updates.&nbsp; I think they are great tools, and in a software environment where everyone is using them, thats great. But in an amateur environment like roll20, I honestly think the roll20 devs should consider banning them from use.&nbsp; Thanks for all the tips @Cassie! I agree with GiGs about using PUG and SCSS, unless it is supported by a game company with a dedicated staff. I don't think it should be banned, but sheet authors need to consider the barrier it will create to others who wishing to contribute to a sheet.&nbsp;
1585718567

Edited 1585718673
GiGs
Pro
Sheet Author
API Scripter
Rabulias said: GiGs, are you saying the rendered HTML and CSS are not readable like "manual" HTML? Would it help prevent problems if the uncompiled PUG and SCSS code was shared in the repo in addition to the rendered? No, PUG and SCSS render perfectly normal and usable html and CSS. But once you start using them on a sheet, they force upgraders into using them to make further updates, and they are much more complex to learn. If someone starts upgrading the sheet without using PUG and SCSS, and someone else uses the PUG/SCSS templates, the later yupdates will overwrite the former. And those using PUG and SCSS will find it difficult to incorporate changes made to the final sheet that aren't made with PUG/SCSS. So they are fine to use (and very powerful and flexible) when only one person is updating a sheet, or when everyone in a project is trained in their use and is committed to using them.&nbsp; But roll20 is a development environment geared to amateur volunteers. Anyone who is maintaining a sheet now has a responsibility to think about what happens when they abandon the project and drift away, or while they are maintaining the sheet and others want to contribute without experience. Both of those things will happen, and roll20 is set up to cope with this and it's actually a benefit. What happens too often with amateur software projects: as they mature, the most committed core of users get more skilled, and start using more advanced tools and techniques. This creates a barrier for entry, so the number of new contributors gradually falls. Then the core users each have changing life circumstances, and gradually move on from the project. And now you have a dying project with a high barrier for entry, which gradually stagnates and dies. There are several sheets now on roll20 that will be impossible to maintain once their current maintainers move on - even a couple you simply cant edit, because their code is obfuscated, and their core files are in offsite repositories. I'm not talking about the By Roll20 sheets-&nbsp; those can and should operate by different rules. It's fine for them to use professional tools, because roll20 has a commitment to maintain them. I'm specifically talking about the community sheets, which should be open to all. Some of them right now are not, because of things like this, and that will increase over time. I think PUG and SCSS are awesome. I'm not a pro in their use, but I've dabbled and see the benefits they bring. I'll use them in my non-roll20 projects. But I think they should not be used here for the reasons in this post. I also think sheets should not be use obfuscated code - everything here should be accessible to amateur volunteers. It's true that not using them makes things harder for the developer, right now in the process of writing a sheet, but we have a responsibility to think beyond ourselves - to think of the health of the community, and its future.
1585720687
vÍnce
Pro
Sheet Author
The PF Community sheet is one such sheet. Do to it's shear size and complexity there was a decision years ago to handle it's code using a proprietary environment that broke the sheet up into various components of code by function.&nbsp; Editing the sheet requires a very specific local environment comprising of node, npm, git, and vsc.&nbsp; This worked as long as there was an active, knowledgeable, programmer at the helm...&nbsp; Eventually that person moved on from the project.&nbsp; While the html and css can be handled outside of this setup, the sheetworker code cannot. I do what I can when I can on the sheet since I'm one of very few people that have the local setup to edit the code.&nbsp; Unfortunately, I'm barely a tinkerer of javascript and the Community sheet has some very complex inner-workings that are way beyond my skill set.&nbsp; As GiGs has mentioned, this is a sheet that has become isolated because it is not easily editable by others.&nbsp; I'm not a programmer but I have a feeling that it's not a simple measure to restructure the sheet into something that could be easily edited by other volunteers.
1585722139
Cassie
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Rabulias&nbsp; Delta Green 2 and Pendragon 5th Edition are two sheets that I've done in PUG. Writing sheets in HTML &amp; CSS does not guarantee they are maintainable or scalable. In fact they make bad programming habits worse by exponential increasing copy/pasted redundant code compared to compiled code. I've disregarded a number of efforts to update poorly programmed community sheets in favor of writing a new sheet in PUG/SCSS. With the programmed I linked above you don't need npm or node. Holding fast to legacy coding practice benefits no one in the long run. Creating fast, high quality, and accessible sheets supports the community.&nbsp;
1585723362

Edited 1585723494
Andreas J.
Forum Champion
Sheet Author
Translator
Cassie said: [..] With the programmed I linked above you don't need npm or node. I assume you mean <a href="https://prepros.io/" rel="nofollow">https://prepros.io/</a> ? Yeah, if it's easy to setup, and we start linking to guide and documentation on them in one single place, and display a streamlined workflow for it. And the Learn Grid section, it says "this should work, haven't rested it" doesn't sound too well for otherwise such an excellent guide, I hope you test it and update the description.
1585761849

Edited 1585761994
Thanks, Cassie! I will take a look and see what I can grok from that. I think I understand some in principle. Awhile back I wrote a VB script to pull details from a database to generate the Skills section of my custom sheet to make consistent coding and account for some fiddly adjustments for certain skills. It was a bit of work up front, but made tweaking that section much easier than doing it manually. The process became 1) Update database, 2) Run script, 3) Copy and paste generated code into sheet HTML. It sounds like PUG does something akin to this for almost the entire sheet, right? Without an external datasource. And thanks for providing resources like this!
1585768142
Cassie
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Rabulias&nbsp; PUG is built off Javascript and so you can do things like Iteration . For skill lists I typically just make an array or object the iterate through it. For some games I'll include an if/else conditional perhaps if a skill needs to have a repeating section instead of just a typical input.
1585775785

Edited 1585776145
Gold
Forum Champion
so many programmer vocabulary words in this thread that are incomprehensible to me. Making a character sheet appears very intimidating (to me, someone who has only about 15 years of HTML experience). Agree with most of what GiGs said with the exception of saying that a professional programmer is not needed now, as it is. It's always been needed. I've been working on my custom character sheet for probably 3 years. And all I want is a super-simple one. The wiki is chock-full of complicated un-defined words. I recognize this thread is a helpful one, and many people do understand it.&nbsp; Most Roll20 users don't understand what this thread is about or how to do any of this. It's just extremely difficult to follow the sentences when you do not understand a lot of terminology. Function?? Git?? Pull?? Branch? Fork? PUG? Array? Object? What I know what a "Repeating Section" is and I don't want to do anything nearly that complicated on my sheet. Just want stuff like NAME: (text field). Strength: (number field). That's all. Riley provided a "kitchen sink" character sheet code in the GITHUB. Kitchen sink is just way too much for a starting point. If one of you could make a super-simple sheet template with literally just fields for NAME, BIOGRAPHY, and STRENGTH NUMBER, that would be great, I will really appreciate it so much. Can you make it have a dark background with lighter text too? People could copy that and make so many game sheets that don't need all the complexity and don't require books-full of learning.
1585787670
vÍnce
Pro
Sheet Author
Gold said: If one of you could make a super-simple sheet template with literally just fields for NAME, BIOGRAPHY, and STRENGTH NUMBER, that would be great, I will really appreciate it so much. Can you make it have a dark background with lighter text too? People could copy that and make so many game sheets that don't need all the complexity and don't require books-full of learning. Maybe GiG's latest pet project might interest you... Sneak Peek: System Agnostic Universal Character Sheet
1585788648

Edited 1585789290
Miguel
Pro
Sheet Author
Pug, SCSS, Less... you name it, are great tools and they do make one's work easier, specially on larger projects. With that said, I agree that they may end up becoming a barrier for non-coders looking to collaborate. I believe a really well thought / coded HTML file would be enough to keep duplication at an acceptable level on smaller projects. That is, keep the number of children nodes at a bare minimum and use flex/grid to make stuff look like what you want. Same with CSS, if you keep you snippets generalized instead specific, you should be good enough without a css compiler. Instead of: &lt;style&gt; .strength { border: 1px solid black; border-radius: 3px; text-align: center; } .strength span { font-weight: bold; } &lt;/style&gt; &lt;div class="strength"&gt;&lt;span name="attr_strength_mod"&gt;&lt;/span&gt;&lt;/div&gt; Go for something like: &lt;style&gt; .box { border: 1px solid black; } .roundCorner { border-radius: 3px; } .text-center { text-align: center; } .text-bold { font-weight: bold; } &lt;/style&gt; &lt;div class="box text-center roundCorner"&gt;&lt;span class="text-bold" name="attr_strength_mod"&gt;&lt;/span&gt;&lt;/div&gt; Also, comment your code as much as you can. That will help out newcomers and probably yourself in the long road. Always aim for readability, less code does not means better code, specially when it comes to maintenance.
1585816640
Andreas J.
Forum Champion
Sheet Author
Translator
Miguel said: That is, keep the number of children nodes at a bare minimum and use flex/grid to make stuff look like what you want. [...] [...] Also, comment your code as much as you can. That will help out newcomers and probably yourself in the long road. Always aim for readability, less code does not means better code, specially when it comes to maintenance. With the sheet's I've created or worked a bunch on in the last 9 months or so, I've tried to follow Google HTML/CSS Style Guide to keep my code as readable as possible, without trying to wrangle it to perfection. I'd be curious on what people think of my " Feast of Legends "(simple sheet) or " Hc Svnt Dracones 2E "(moderate size, more organized)-sheets. Neither are efficiently written, but are organized and commented in a way that more than makes up for being less effective. If anyone would happen to take one or two minutes to skim through them and give an opinion, I'd greatly appreciate it. I usually try to have a bunch of general CSS, and when I create each section I end up creating adjustments to each, only at times going back and bundling things I repeat to be general styles. Miguel said: Same with CSS, if you keep you snippets generalized instead specific, you should be good enough without a css compiler. Sure, it might be good enough, but the simple fact that you can nest things with SCSS reduce the lines of code you work with in CSS, which already by itself should be a good enough reason to try use it even if you'd otherwise use no other features of it. Which reminds me, I really should try that with some of my sheets, as a starting point. The " Building Character Sheets "- wiki page has a " Best Practice "-section(and by extension, the help-center version ), that I've been heavily involved in writing. It contains similar advice, split by topics, but is starting to become somewhat long if much more of advice here would be added. Would be great if some of the fine folks here could take a second look at it. As the main article have grown, I've tried to create new pages for sections that grows too large, and there is starting to be a good bunch of advice here that might work better as a separate page linked to the page. Maybe I'll get started on moving some of the things to the wiki at some point. When I get started with using PUG or SCSS in my sheets, I might make a short guide on the things.
1585823092
Miguel
Pro
Sheet Author
Andreas, I will take a look at the sheets you've mentioned and let you know what I think. Yes, compilers are wonderful tools, I am not against their use at all, in fact, I am a happy user of the combo pointed out by Cassie. However, for smaller projects it maybe an overkill. Also, the easiness they provide sometimes may lead to less reflection (as in reflect while coding). Taking your example, SCSS, LESS, etc., make very easy to deal with nesting, and that may lead to lots of nesting on your css file. However, nesting goes in the opposite direction of generalization which is one of the core foundation to make your css reusable ;)
1585835017

Edited 1585835181
Miguel said: Same with CSS, if you keep you snippets generalized instead specific, you should be good enough without a css compiler. I strongly disagree with the conclusion you got to here. Yes, you don't want to be overly-specific, but I think being overly-general is even worse. It becomes only marginally better than just using the "style" attribute. I use and appreciate the Bootstrap library in my professional work, but the components (and creating your own components) are more important than the utility classes.&nbsp; There's a reason Bootstrap has things like not only "btn" but "btn-primary". The designer shouldn't be expected to remember what set of classes to use on a particular item to get it styled the way they want. That's what CSS is for. General utility classes like "box", "roundCorner", "center", and "bold" in your example should be used for one-off (or seldom or inconsistently repeated) sections of the page. Maybe you want to highlight a particular element distinct from the "norm", or you have only one instance of what may otherwise become a component. For any page, you'll want to start with the universal styling.&nbsp; Decide the default for all buttons, all labels, all text inputs, and so on. Rather than adding utility classes to every instance, you'll want to define these with CSS rules.&nbsp; Buttons are bold and rounded, labels are bold and underlined, that sort of thing. The next step is classes for universal styling. Not to the degree you've done with "box" and "text-bold", but effectively the same as above just using a class rather than an element to define the item. They still speak to the intent &nbsp;of the item rather than how it should look. For example making all ".subheader" a certain font size and bold, or making all "button.checkbox" look like a checkbox. The step above that is component classes used to describe a repeated set of elements.&nbsp; I agree, of course, that a "strength" component is too specific. But a "primary-attribute" component is not. It is typical to want consistent styling on the "Strength", "Agility", "Intelligence", etc attributes on a sheet. If you're consistent in the HTML structure of your primary-attribute components (and you really should be), then you probably won't need a class for every element in it either. &lt;div class="columns"&gt; &lt;div class="primary-attribute-category"&gt; &lt;div class="primary-attribute"&gt; &lt;div class="subheader"&gt; &lt;input type="hidden" class="flag" name="attr_strength_flag" value="0" /&gt; &lt;button type="action" class="toggle" name="act_strength" data-i18n="strength"&gt;Strength&lt;/button&gt; &lt;/div&gt; &lt;div class="dots"&gt; &lt;input type="hidden" class="dot" name="attr_strength" value="1" /&gt; &lt;button type="action" class="dot" name="act_strength_1"&gt;&lt;/button&gt; &lt;button type="action" class="dot gt-1" name="act_strength_2"&gt;&lt;/button&gt; &lt;button type="action" class="dot gt-1 gt-2" name="act_strength_3"&gt;&lt;/button&gt; &lt;button type="action" class="dot gt-1 gt-2 gt-3" name="act_strength_4"&gt;&lt;/button&gt; &lt;button type="action" class="dot gt-1 gt-2 gt-3 gt-4" name="act_strength_5"&gt;&lt;/button&gt; &lt;/div&gt; &lt;/div&gt; &lt;div class="primary-attribute"&gt; &lt;div class="subheader"&gt; &lt;input type="hidden" class="flag" name="attr_dexterity_flag" value="0" /&gt; &lt;button type="action" class="toggle" name="act_dexterity" data-i18n="dexterity"&gt;Dexterity&lt;/button&gt; &lt;/div&gt; &lt;div class="dots"&gt; &lt;input type="hidden" class="dot" name="attr_dexterity" value="1" /&gt; &lt;button type="action" class="dot" name="act_dexterity_1"&gt;&lt;/button&gt; &lt;button type="action" class="dot gt-1" name="act_dexterity_2"&gt;&lt;/button&gt; &lt;button type="action" class="dot gt-1 gt-2" name="act_dexterity_3"&gt;&lt;/button&gt; &lt;button type="action" class="dot gt-1 gt-2 gt-3" name="act_dexterity_4"&gt;&lt;/button&gt; &lt;button type="action" class="dot gt-1 gt-2 gt-3 gt-4" name="act_dexterity_5"&gt;&lt;/button&gt; &lt;/div&gt; &lt;/div&gt; &lt;div class="primary-attribute"&gt; &lt;div class="subheader"&gt; &lt;input type="hidden" class="flag" name="attr_stamina_flag" value="0" /&gt; &lt;button type="action" class="toggle" name="act_stamina" data-i18n="stamina"&gt;Stamina&lt;/button&gt; &lt;/div&gt; &lt;div class="dots"&gt; &lt;input type="hidden" class="dot" name="attr_stamina" value="1" /&gt; &lt;button type="action" class="dot" name="act_stamina_1"&gt;&lt;/button&gt; &lt;button type="action" class="dot gt-1" name="act_stamina_2"&gt;&lt;/button&gt; &lt;button type="action" class="dot gt-1 gt-2" name="act_stamina_3"&gt;&lt;/button&gt; &lt;button type="action" class="dot gt-1 gt-2 gt-3" name="act_stamina_4"&gt;&lt;/button&gt; &lt;button type="action" class="dot gt-1 gt-2 gt-3 gt-4" name="act_stamina_5"&gt;&lt;/button&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;div class="horizontal-spacer"&gt;&lt;/div&gt; &lt;!-- more attribute categories... --&gt; &lt;/div&gt; .sheet-columns { display: flex; &gt; :not(.sheet-horizontal-spacer) { flex: 1; } } .sheet-horizontal-spacer { padding: 0 15px; } .sheet-subheader { font-size: 1.4em; font-weight: bold; text-transform: uppercase; } button.sheet-toggle { padding: 0; border-style: none; background: transparent; font-weight: bold; } input.sheet-flag[value="1"] ~ button.sheet-toggle { color: #2A2D80; text-shadow: 0 0 10px black; } .sheet-dots { display: flex; } button.sheet-dot { padding: 0; border: solid 1px #a8a8a8; cursor: pointer; width: 14px; height: 14px; border-radius: 50%; } input.sheet-dot { &amp;[value="1"] ~ button.sheet-dot:not(.sheet-gt-1) { background: #404040; } &amp;[value="2"] ~ button.sheet-dot:not(.sheet-gt-2) { background: #404040; } &amp;[value="3"] ~ button.sheet-dot:not(.sheet-gt-3) { background: #404040; } &amp;[value="4"] ~ button.sheet-dot:not(.sheet-gt-4) { background: #404040; } } .sheet-primary-attribute-category { border: 1px solid black; padding: 3px; } .sheet-primary-attribute { display: flex; &gt; .sheet-subheader { flex: 1; } } The goal should be creating reusable components .&nbsp; It is that goal that PUG and SCSS really help contribute to.
1585838369
Miguel
Pro
Sheet Author
Zed, I was just trying to illustrate the idea of generalization. I was not trying to imply one should create a different rule for every single thing, but rather creating rules, in a logical way, for properties that keep appearing across different places, in the same way you do with a mixin. Same concept of refactoring a code, you extract the method and make it reusable. I am not against SCSS and PUG, I use them in most of my project nowadays when there is enough complexity to justify it (same thing goes for JS frameworks.) However, GiGs has a point and I was just trying to make clear you don't need a compiler to produce good quality code, specially if you are doing it once for a small scope charsheet. With that said, if your team agrees on a set of tools that speed up your workflow or you happen to be the author of a bunch of different charsheets, you will sooner or later end up looking for shared libraries, components, etc.
Rather than PUG, how would people here feel about using&nbsp; Handlebars ?&nbsp; If there's not already a handy CLI to construct partials from files, I can probably create one.&nbsp; The goal would be to have something like: &lt;div class="columns"&gt; {{#each primaryAttributeCategories as |primaryAttributeCategory}} &lt;div class="primary-attribute-category"&gt; {{#each primaryAttributeCategory.attributes as |primaryAttribute}} &lt;div class="primary-attribute"&gt; &lt;div class="subheader"&gt; &lt;input type="hidden" class="flag" name="attr_{{primaryAttribute}}_flag" value="0" /&gt; &lt;button type="action" class="toggle" name="act_{{primaryAttribute}}" data-i18n="{{primaryAttribute}}"&gt;{{toTitleCase primaryAttribute}}&lt;/button&gt; &lt;/div&gt; {{&gt; dots attr=primaryAttribute count=5}} &lt;/div&gt; {{/each}} {{#unless @last}} &lt;div class="horizontal-spacer"&gt;&lt;/div&gt; {{/unless}} &lt;/div&gt; {{/each}} &lt;/div&gt; This would be supplied with data: { primaryAttributeCategories: [ { name: "mental", attributes: [ "intelligence", "wits", "resolve" ], { name: "physical", attributes: [ "strength", "dexterity", "stamina" ], { name: "social", attributes: [ "presence", "manipulation", "composure" ], ] } In this example, the "dots" partial would be defined elsewhere for re-use. With a handy CLI, the designer would just have to execute something like npm run handlebars-execute base-template.html -d data.json And just like that, an entire chunk of otherwise-repeated code can be generated! At least that's the plan. I'll have to set up a proof-of-concept.
1585845257
Kraynic
Pro
Sheet Author
I'm probably in a similar camp to Gold with this.&nbsp; I have a fairly simple sheet that took me 1.5 (maybe closer to 2?) years to get to the current place it is, primarily through using it in play.&nbsp; For someone that barely has a handle on html and css, adding other tools on top of it that I don't understand probably aren't going to be helpful.&nbsp; If something like this becomes a requirement, I'll see if I can muddle through, but may very well just need to bow out of doing anything with sheets. I guess some of this depends on what background your sheet authors are coming from as well.&nbsp; The only exposure I have to this stuff is for writing a sheet.&nbsp; If someone has anything related to this as part of their daily life, I expect picking all this up is much easier.
1585858287

Edited 1585858358
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Primal Zed, Does handlebars allow logic? I know moustache doesn't. And, while I hate that PUG uses whitespace for scoping/nesting of elements, having if/else/else if capability is a big help for more complicated sheets. Alternatively, Cassie, how do you handle the whitespace scoping of PUG? So far, I haven't found a good way to make sure I'm at the level I actually want and frequently wind up with code that is improperly nested. If TWIG wasn't a PHP templating language, I'd just use that, but alas...
1585872045

Edited 1585872092
Scott C.&nbsp; If by logic then you mean if/then/else, then&nbsp; yes it does . There's also a library with a bunch of helpers that I'm pulling in:&nbsp; <a href="https://github.com/helpers/handlebars-helpers#helpers" rel="nofollow">https://github.com/helpers/handlebars-helpers#helpers</a> I'm pretty close to having a working example to share.
1585872837
Cassie
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Scott C. I never have that issue using Sublime and/or VS Code. Do you not keep little guiding lines on? &nbsp;
1585877467

Edited 1585877482
I created a separate topic with an example using Handlebars. Please let me know what you guys think. <a href="https://app.roll20.net/forum/post/8380005/sheet-templating-with-handlebars" rel="nofollow">https://app.roll20.net/forum/post/8380005/sheet-templating-with-handlebars</a> (I hope I'm not violating rules on branching to a new topic or cross-posting - it seemed appropriate here.)
1585881303
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Cassie, I do, but once the container is off the screen, remembering which line is what is unreliable. Primal Zen, that sounds interesting.
1585891622
Cassie
Forum Champion
Sheet Author
API Scripter
Compendium Curator
By 'off the screen' do you mean if not using Word Wrap? Never encountered this scenario.
1585919243
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
No, say you have a very long sesctiom, now your container div (say .whole) is far enough up that you have to scroll up to see it. Add in several containers doing this and keeping track of here your cursor needs to be is nearly impossible when you need to put something outside of more than 1 or 2 containers.
Correct me if I'm wrong, but I think the concern from Scott C. is that there are no closing tags. Instead, you have to line up the indentation correctly to close or exit a given element.&nbsp; Making a somewhat longer example, I can understand getting lost on which element I'm adding to. Guide lines from the text editor are a huge boon here, especially if the text editor also highlights the guide line going back up to the current element you're in. I'd almost say it's required. div(class="header") img div(class="content") div(class="tabs") button(class="traits_tab") Traits button(class="notes_tab") Notes button(class="other_tab") Other div(class="traits_content") div(class="attributes) div(class="attribute) span stuff div(class="attribute") span stuff div(class="skills") div("class="skill") span stuff input(type="text") div("class="skill") span stuff input(type="text") div("class="skill") span stuff input(type="text") div(class="notes_content") div(class="bio") textarea div(class="notes") fieldset(class="repeating_notes") div span Title input(type="text" placeholder="Title") textarea div(class="contacts") fieldset("class="repeating_contacts") div span Name input(type="text" placeholder="Name") div span Relationship input("type="text" placeholder="Relationship") div(class="other_content") div other stuff i don't know div(class="footer") span stuff
1585923844
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Yep, element zero has it.
1585924737

Edited 1585925354
Cassie
Forum Champion
Sheet Author
API Scripter
Compendium Curator
I'd recommend Mixins and loops more. If we're using Zed example the number of lines could be cut in half by running a loop over an array with the tabs, attributes, and skills. I even use a Mixins for nearly all my inputs since they are almost always a consistent style of&nbsp; label with an input inside for accessibility. If following good programming habits your sheet should be broken down into small pieces, modules , and imported into the main file to be more maintainable. Though I'll admit I'm just starting to get into the habit of the this myself. I'll provide an example when I've done it. ;)
Yeah I haven't really used Pug and was just making something up to illustrate what I perceived to be the concern.
1585925582
Miguel
Pro
Sheet Author
I was never a problem for me, maybe due to the fact that I have a very wide display that I use in portrait mode + visual code with pug highlighting. If I have to point something on pug that used to bother me, was the fact that it does not resemble the language it compiles into. I mean, you cant mixture both things, well you can by importing or auto-converting to pug on paste, but that is not straight forward as the option proposed by Zed. In someway it feels like someone looking for a CSS compiler and having to pick between Sass and SCSS. I would probably recommend the second one. One thing I appreciated about Zeds idea is the possibility of having a Model that could be used by both the View and ViewModel. That should be possible, right Zed?
Miguel&nbsp; I wouldn't say that it establishes a Model-View-ViewModel architecture, no. MVVM is for ongoing updates of a graphical user interface with two-way binding, and that is not what we're doing here. If MVVM can be applied anywhere on these sheets, it will be in the relationship between the HTML and the worker scripts. Both Pug and Handlebars are templating tools, and our proposed use of them is for generating static files from a template and a data set. It's more like setting up a design and then publishing a deliverable product based on that design. I think comparing these templating tools to SASS and SCSS is actually kind of appropriate: Pug, like SASS, has its own syntax for generating the desired file, specifically using headers and indentation for nesting. Handlebars, like SCSS, can include the same syntax as the file it generates, and its enhancements are encapsulated within open- and close- tags rather for nesting. Unlike SASS and SCSS, Handlebars is not specifically an extension or enhancement of Pug. They are distinct products, while SCSS is literally "SASS-y CSS".
1585931671

Edited 1585931785
Miguel
Roll20 Production Team
Sheet Author
I know it does not establishes&nbsp;that, what I meant is that I could do my Model (json) use that as source to build the UI using handlebars like you shown, import the json itself as part of the sheetworkers.js and use it there also. In my particular case, I can see this being really useful, using the example you've posted above: { primaryAttributeCategories: [ { name: "mental", attributes: [ "intelligence", "wits", "resolve" ], { name: "physical", attributes: [ "strength", "dexterity", "stamina" ], { name: "social", attributes: [ "presence", "manipulation", "composure" ], ] } to give a really simple example, on my worker I could have something like: primaryAttributeCategories.forEach(category =&gt; { &nbsp; &nbsp; category.attributes.forEach(attribute =&gt; { &nbsp; &nbsp; &nbsp; &nbsp; on(`change:${attribute}`, eventInfo =&gt; { &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; }); }); if I wanted to add a new attribute to the sheet I would just need to modify my Model (supposing my css is flexible enough). Hope that makes it clear.
1585932885

Edited 1585932951
Miguel&nbsp; If you want the text/worker scripts use the same data from the Handlebars template, you can use the JSONstringify &nbsp;helper to add it in: Template (note using triple-brackets rather than double-brackets so it doesn't html-escape the value) : &lt;div&gt;HTML Stuff&lt;/div&gt; &lt;script type="text/worker"&gt; const data = {{{JSONstringify @root}}}; data.primaryAttributeCategories.forEach(category =&gt; { /* ... */ }); &lt;/script&gt; Input: { "primaryAttributeCategories": [ { "name": "mental", "attributes": ["intelligence", "wits", "resolve"] }, { "name": "physical", "attributes": ["strength", "dexterity", "stamina"] }, { "name": "social", "attributes": ["presence", "manipulation", "composure"] } ] } You can try this out from&nbsp; <a href="https://handlebarsjs.com/playground.html" rel="nofollow">https://handlebarsjs.com/playground.html</a> &nbsp;you'll just have to add to the `Preparation-Script` section (the goal with proper tooling is contributors will not have to know about this step, let alone add it themselves): Handlebars.registerHelper("JSONstringify", function(obj) { return JSON.stringify(obj); }); I'm sure that you can do pretty much the same thing in Pug.
1585933901

Edited 1585933943
Miguel
Pro
Sheet Author
Primal Zed Thanks. I will check if it is possible to accomplish this with Pug nowadays. Last time I checked, you needed gulp in order to do it.
1586024815
Andreas J.
Forum Champion
Sheet Author
Translator
I've made a wiki page to collect misc. Sheet Author Tips like this. Also updated several wiki pages.