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

Outputting CSS when editing character bio tab from script

I have been working on a script to automate specialized inventory management for one of my campaigns.  The script outputs HTML to the bio of a specially selected character sheet. This works correctly. However, attempting to style that HTML has failed.  The relevant output is in the main script on lines 151-187 .  I am struggling to determine why the CSS is not being read. I tested the HTML output as a standalone file, and the CSS correctly picks up. Is there another style that the Bio & Info text is using which overrides my styles? Or is CSS simply blocked from being output by scripts in this way?
1671075558
timmaugh
Pro
API Scripter
If I understand correctly, I think your problem is that the Bio field isn't a fully functional DOM where your CSS rules can be interpreted. They have to be inlined. You can still manage a modicum of abstraction and composability by declaring your CSS as properties of a JS object, and then building your HTML using factory functions. I lifted and adapted this from others in the House of Mod, but it would basically look like: // CSS ======================================= const defaultThemeColor1 = '#66806a'; const css = {         tb: {             width: '100%',             margin: '0 auto',             'border-collapse': 'collapse',             'font-size': '12px',         },         p: {},         a: {},         img: {},         h1: {},         h2: {},         h3: {},         h4: {},         h5: {},         th: {             'border-bottom': `1px solid #000000`,             'font-weight': `bold`,             'text-align': `center`,             'line-height': `22px`         },         tr: {},         td: {             padding: '4px',             'min-width': '10px'         },         code: {},         pre: {             'color': 'dimgray',             'background': 'transparent',             'border': 'none',             'white-space': 'pre-wrap',             'font-family': 'Inconsolata, Consolas, monospace'         },         div: {             'background-color': '#000000',             'overflow': `hidden`         },         divContainer: {             'background-color': '#000000',             'overflow': `hidden`,             width: '100%',             border: 'none'         },         rounded: {             'border-radius': `10px`,             'border': `2px solid #000000`,         },  //... }; You can see I establish a default color (I would probably do more with defaults here, so that they could be reused throughout the CSS), and then I declare "rules" for basic structural tags and for "classes" (like "rounded"). Then you'd have an html factory like:     const combineCSS = (origCSS = {}, ...assignCSS) => {         return Object.assign({}, origCSS, assignCSS.reduce((m, v) => {             return Object.assign(m, v || {});         }, {}));     };     const assembleCSS = (css) => {         return `"${Object.keys(css).map((key) => { return `${key}:${css[key]};` }).join('')}"`;     };     // HTML =======================================     const html = {         div: (content, ...CSS) => `<div style=${assembleCSS(combineCSS(css.div, ...CSS))}>${content}</div>`,         h1: (content, ...CSS) => `<h1 style=${assembleCSS(combineCSS(css.h1, ...CSS))}>${content}</h1>`,         h2: (content, ...CSS) => `<h2 style=${assembleCSS(combineCSS(css.h2, ...CSS))}>${content}</h2>`,         h3: (content, ...CSS) => `<h3 style=${assembleCSS(combineCSS(css.h3, ...CSS))}>${content}</h3>`,         h4: (content, ...CSS) => `<h4 style=${assembleCSS(combineCSS(css.h4, ...CSS))}>${content}</h4>`,         h5: (content, ...CSS) => `<h5 style=${assembleCSS(combineCSS(css.h5, ...CSS))}>${content}</h5>`,         p: (content, ...CSS) => `<p style=${assembleCSS(combineCSS(css.p, ...CSS))}>${content}</p>`,         table: (content, ...CSS) => `<table style=${assembleCSS(combineCSS(css.tb, ...CSS))}>${content}</table>`,         th: (content, ...CSS) => `<th style=${assembleCSS(combineCSS(css.th, ...CSS))}>${content}</th>`,         tr: (content, ...CSS) => `<tr style=${assembleCSS(combineCSS(css.tr, ...CSS))}>${content}</tr>`,         td: (content, ...CSS) => `<td style=${assembleCSS(combineCSS(css.td, ...CSS))}>${content}</td>`,         td2: (content, ...CSS) => `<td colspan="2" style=${assembleCSS(combineCSS(css.td, ...CSS))}>${content}</td>`,         code: (content, ...CSS) => `<code style=${assembleCSS(combineCSS(css.code, ...CSS))}>${content}</code>`,         pre: (content, ...CSS) => `<pre style=${assembleCSS(combineCSS(css.pre, ...CSS))}>${content}</pre>`,         span: (content, ...CSS) => `<span style=${assembleCSS(combineCSS(css.span, ...CSS))}>${content}</span>`,         a: (content, link, ...CSS) => `<a href="${link}" style=${assembleCSS(combineCSS(css.a, ...CSS))}>${content}</a>`,         img: (content, altText, ...CSS) => `<img src="${content}" alt="${altText}" style=${assembleCSS(combineCSS(css.img, ...CSS))}>`,         tip: (content, tipText, ...CSS) => `<span class="showtip tipsy-n-right" title="${HE(HE(tipText))}"style=${assembleCSS(combineCSS(css.span, ...CSS))}>${content}</span>`     }; Where the HE() function is Aaron's HTML escape function:     // HTML Escaping function     const HE = (() => {         const esRE = (s) => s.replace(/(\\|\/|\[|\]|\(|\)|\{|\}|\?|\+|\*|\||\.|\^|\$)/g, '\\$1');         const e = (s) => `&${s};`;         const entities = {             '<': e('lt'),             '>': e('gt'),             "'": e('#39'),             '@': e('#64'),             '{': e('#123'),             '|': e('#124'),             '}': e('#125'),             '[': e('#91'),             ']': e('#93'),             '"': e('quot')         };         const re = new RegExp(`(${Object.keys(entities).map(esRE).join('|')})`, 'g');         return (s) => s.replace(re, (c) => (entities[c] || c));     })(); To use all of that, you'd build your html as a series of nested tags: let output = html.div(html.h1('Header Content') + html.div('This is the main content'), css.divContainer, css.rounded, { border: '2px red solid'}); You can pass established css rule objects or new ad hoc objects. Because the html factory uses the combineCSS function, the last CSS rules applied get written to the style object last, meaning they act just like more specific CSS rules taking precedence over less specific references. And the assembleCSS function turns the style object into actual inline css rules for the html element you want to style. Your CSS is still reusable even though you are building your presentation components individually and rendering the styles individuall/inline.
1671075661
timmaugh
Pro
API Scripter
Again, most of that structure isn't mine. I added the combineCSS function and added the spread operator to allow multiple css rule objects being passed to an element. Most of that was shown to me by Scott, who I think got it from Aaron. But I could be wrong about that.
Thanks for the tips, it's seriously helpful.