diff --git a/.eslintrc.js b/.eslintrc.js
index dd4bcd0d3..74e7bb660 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -15,7 +15,7 @@ module.exports = {
rules : {
/** Errors **/
'camelcase' : ['error', { properties: 'never' }],
- 'func-style' : ['error', 'expression', { allowArrowFunctions: true }],
+ //'func-style' : ['error', 'expression', { allowArrowFunctions: true }],
'no-array-constructor' : 'error',
'no-iterator' : 'error',
'no-nested-ternary' : 'error',
diff --git a/.stylelintrc.json b/.stylelintrc.json
new file mode 100644
index 000000000..207dfda62
--- /dev/null
+++ b/.stylelintrc.json
@@ -0,0 +1,48 @@
+{
+ "extends": [
+ "stylelint-config-recess-order",
+ "stylelint-config-recommended"],
+ "plugins": [
+ "stylelint-stylistic",
+ "./stylelint_plugins/declaration-colon-align.js",
+ "./stylelint_plugins/declaration-colon-min-space-before",
+ "./stylelint_plugins/declaration-block-multi-line-min-declarations"
+ ],
+ "customSyntax": "postcss-less",
+ "rules": {
+ "no-descending-specificity" : null,
+ "at-rule-no-unknown" : null,
+ "function-no-unknown" : null,
+ "font-family-no-missing-generic-family-keyword" : null,
+ "font-weight-notation" : "named-where-possible",
+ "font-family-name-quotes" : "always-unless-keyword",
+ "stylistic/indentation" : "tab",
+ "no-duplicate-selectors" : true,
+ "stylistic/color-hex-case" : "upper",
+ "color-hex-length" : "long",
+ "stylistic/selector-combinator-space-after" : "always",
+ "stylistic/selector-combinator-space-before" : "always",
+ "stylistic/selector-attribute-operator-space-before" : "never",
+ "stylistic/selector-attribute-operator-space-after" : "never",
+ "stylistic/selector-attribute-brackets-space-inside" : "never",
+ "selector-attribute-quotes" : "always",
+ "selector-pseudo-element-colon-notation" : "double",
+ "stylistic/selector-pseudo-class-parentheses-space-inside" : "never",
+ "stylistic/block-opening-brace-space-before" : "always",
+ "naturalcrit/declaration-colon-min-space-before" : 1,
+ "stylistic/declaration-block-trailing-semicolon" : "always",
+ "stylistic/declaration-colon-space-after" : "always",
+ "stylistic/number-leading-zero" : "always",
+ "function-url-quotes" : ["always", { "except": ["empty"] }],
+ "function-url-scheme-disallowed-list" : ["data","http"],
+ "comment-whitespace-inside" : "always",
+ "stylistic/string-quotes" : "single",
+ "stylistic/media-feature-range-operator-space-before" : "always",
+ "stylistic/media-feature-range-operator-space-after" : "always",
+ "stylistic/media-feature-parentheses-space-inside" : "never",
+ "stylistic/media-feature-colon-space-before" : "always",
+ "stylistic/media-feature-colon-space-after" : "always",
+ "naturalcrit/declaration-colon-align" : true,
+ "naturalcrit/declaration-block-multi-line-min-declarations": 1
+ }
+}
diff --git a/Dockerfile b/Dockerfile
index 67b42b9dc..82b13ac86 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM node:16.13-alpine
+FROM node:18-alpine
RUN apk --no-cache add git
ENV NODE_ENV=docker
diff --git a/changelog.md b/changelog.md
index c40aa625b..626a992d9 100644
--- a/changelog.md
+++ b/changelog.md
@@ -80,6 +80,97 @@ pre {
## changelog
For a full record of development, visit our [Github Page](https://github.com/naturalcrit/homebrewery).
+### Thursday 17/08/2023 - v3.9.2
+{{taskList
+
+##### Calculuschild
+
+* [x] Fix links to certain old Google Drive files
+
+Fixes issue [#2917](https://github.com/naturalcrit/homebrewery/issues/2917)
+
+##### G-Ambatte
+
+* [x] Menus now open on click, and internally consistent
+
+Fixes issue [#2702](https://github.com/naturalcrit/homebrewery/issues/2702), [#2782](https://github.com/naturalcrit/homebrewery/issues/2782)
+
+* [x] Add smarter footer snippet
+
+Fixes issue [#2289](https://github.com/naturalcrit/homebrewery/issues/2289)
+
+* [x] Add sanitization in Style editor
+
+Fixes issue [#1437](https://github.com/naturalcrit/homebrewery/issues/1437)
+
+* [x] Rework class table snippets to remove unnecessary randomness
+
+Fixes issue [#2964](https://github.com/naturalcrit/homebrewery/issues/2964)
+
+* [x] Add User Page link to Google Drive file for file owners, add icons for additional storage locations
+
+Fixes issue [#2954](https://github.com/naturalcrit/homebrewery/issues/2954)
+
+* [x] Add default save location selection to Account Page
+
+Fixes issue [#2943](https://github.com/naturalcrit/homebrewery/issues/2943)
+
+##### 5e-Cleric
+
+* [x] Exclude cover pages from Table of Content generation (editing on mobile is still not recommended)
+
+Fixes issue [#2920](https://github.com/naturalcrit/homebrewery/issues/2920)
+
+##### Gazook89
+
+* [x] Adjustments to improve mobile viewing
+
+}}
+
+### Wednesday 28/06/2023 - v3.9.1
+{{taskList
+
+##### G-Ambatte
+
+* [x] Better error pages with more useful information
+
+Fixes issue [#1924](https://github.com/naturalcrit/homebrewery/issues/1924)
+}}
+
+### Friday 02/06/2023 - v3.9.0
+{{taskList
+
+##### Calculuschild
+
+* [x] Fix some files not showing up on userpage when user has a large number of brews in Google Drive
+
+Fixes issue [#2408](https://github.com/naturalcrit/homebrewery/issues/2408)
+
+* [x] Pressing tab now indents with spaces instead of tab character; fixes several issues with Markdown lists
+
+Fixes issues [#2092](https://github.com/naturalcrit/homebrewery/issues/2092), [#1556](https://github.com/naturalcrit/homebrewery/issues/1556)
+
+* [x] Rename `naturalCritLogo.svg` to `naturalCritLogoRed.svg`. Those using the {{beta BETA}} coverPage snippet may need to update that text to make the NaturalCrit logo appear again.
+
+##### G-Ambatte
+
+* [x] Fix strange animation of image masks
+
+Fixes issue [#2790](https://github.com/naturalcrit/homebrewery/issues/2790)
+
+##### 5e-Cleric
+
+* [x] New {{openSans **PHB → {{fac,book-part-cover}} PART COVER PAGE** }} snippet for V3!
+
+* [x] New {{openSans **PHB → {{fac,book-back-cover}} BACK COVER PAGE** }} snippet for V3! (Thanks to /u/Kaiburr_Kath-Hound on Reddit for providing some of these resources!)
+
+* [x] New {{openSans **TEXT EDITOR → {{fas,fa-bars}} INDEX** }} snippet for V3!
+
+* [x] Fix highlighting of curly braces inside comments
+
+Fixes issue [#2784](https://github.com/naturalcrit/homebrewery/issues/2784)
+}}
+
### Wednesday 12/04/2023 - v3.8.0
{{taskList
@@ -101,7 +192,7 @@ Fixes issue [#2595](https://github.com/naturalcrit/homebrewery/issues/2595)
Fixes issues [#2657](https://github.com/naturalcrit/homebrewery/issues/2657)
-* [x] Fix internal links inside `
` blocks not receiving the `target=_self` attribute
+* [x] Fix internal links inside `<\div>` blocks not receiving the `target=_self` attribute
Fixes issues [#2680](https://github.com/naturalcrit/homebrewery/issues/2680)
@@ -111,7 +202,7 @@ Fixes issues [#1679](https://github.com/naturalcrit/homebrewery/issues/1679)
* [x] Add local Windows install script via Chocolatey
-##### 5e-Clerc
+##### 5e-Cleric
* [x] New {{openSans **TABLES → {{fas,fa-language}} RUNE TABLE**}} snippets for V3. Adds an alphabetic script translation table.
diff --git a/client/homebrew/brewRenderer/brewRenderer.jsx b/client/homebrew/brewRenderer/brewRenderer.jsx
index 27fef7e16..51921c8ca 100644
--- a/client/homebrew/brewRenderer/brewRenderer.jsx
+++ b/client/homebrew/brewRenderer/brewRenderer.jsx
@@ -108,6 +108,12 @@ const BrewRenderer = createClass({
return false;
},
+ sanitizeScriptTags : function(content) {
+ return content
+ .replace(/';
- const rendered = Markdown.render(source);
- expect(rendered).toMatch('<script></script>');
-});
-
test('Processes the markdown within an HTML block if its just a class wrapper', function() {
const source = '
*Bold text*
';
const rendered = Markdown.render(source);
diff --git a/themes/Legacy/5ePHB/snippets/tableOfContents.gen.js b/themes/Legacy/5ePHB/snippets/tableOfContents.gen.js
index 4082ac4ef..40d64af22 100644
--- a/themes/Legacy/5ePHB/snippets/tableOfContents.gen.js
+++ b/themes/Legacy/5ePHB/snippets/tableOfContents.gen.js
@@ -47,8 +47,8 @@ const getTOC = (pages)=>{
return res;
};
-module.exports = function(brew){
- const pages = brew.text.split('\\page');
+module.exports = function(props){
+ const pages = props.brew.text.split('\\page');
const TOC = getTOC(pages);
const markdown = _.reduce(TOC, (r, g1, idx1)=>{
r.push(`- **[${idx1 + 1} ${g1.title}](#p${g1.page})**`);
diff --git a/themes/V3/5eDMG/style.less b/themes/V3/5eDMG/style.less
index ffc73d992..2ced98312 100644
--- a/themes/V3/5eDMG/style.less
+++ b/themes/V3/5eDMG/style.less
@@ -10,6 +10,21 @@
background-image : url(/assets/DMG_background.png);
background-size : cover;
+ /*TABLES WITHIN NOTES*/
+ .note table tbody tr:nth-child(odd) {
+ background:#fff;
+ }
+
+ /*DROP CAP*/
+ h1 + p::first-letter {
+ background-image: unset;
+ color:black;
+ }
+
+ .quote p:first-child::first-line {
+ all: unset;
+ }
+
&:after {
background-image : url(/assets/DMG_footerAccent.png);
height: 58px;
@@ -25,4 +40,4 @@
.partCover {
background-image: @partCoverHeaderDMG;
}
-}
\ No newline at end of file
+}
diff --git a/themes/V3/5ePHB/snippets.js b/themes/V3/5ePHB/snippets.js
index 4e88a7a22..c0933d70d 100644
--- a/themes/V3/5ePHB/snippets.js
+++ b/themes/V3/5ePHB/snippets.js
@@ -6,8 +6,9 @@ const MonsterBlockGen = require('./snippets/monsterblock.gen.js');
const scriptGen = require('./snippets/script.gen.js');
const ClassFeatureGen = require('./snippets/classfeature.gen.js');
const CoverPageGen = require('./snippets/coverpage.gen.js');
-const PartCoverPageGen = require('./snippets/partcoverpage.gen.js');
const TableOfContentsGen = require('./snippets/tableOfContents.gen.js');
+const indexGen = require('./snippets/index.gen.js');
+const QuoteGen = require('./snippets/quote.gen.js');
const dedent = require('dedent-tabs').default;
@@ -19,20 +20,16 @@ module.exports = [
icon : 'fas fa-pencil-alt',
view : 'text',
snippets : [
- {
- name : 'Page Number',
- icon : 'fas fa-bookmark',
- gen : '{{pageNumber 1}}\n{{footnote PART 1 | SECTION NAME}}\n\n'
- },
- {
- name : 'Auto-incrementing Page Number',
- icon : 'fas fa-sort-numeric-down',
- gen : '{{pageNumber,auto}}\n{{footnote PART 1 | SECTION NAME}}\n\n'
- },
{
name : 'Table of Contents',
icon : 'fas fa-book',
gen : TableOfContentsGen
+ },
+ {
+ name : 'Index',
+ icon : 'fas fa-bars',
+ gen : indexGen,
+ experimental : true
}
]
},
@@ -127,6 +124,11 @@ module.exports = [
icon : 'fas fa-mask',
gen : ClassFeatureGen,
},
+ {
+ name : 'Quote',
+ icon : 'fas fa-quote-right',
+ gen : QuoteGen,
+ },
{
name : 'Note',
icon : 'fas fa-sticky-note',
@@ -188,6 +190,12 @@ module.exports = [
gen : CoverPageGen.part,
experimental : true
},
+ {
+ name : 'Back Cover Page',
+ icon : 'fac book-back-cover',
+ gen : CoverPageGen.back,
+ experimental : true
+ },
{
name : 'Magic Item',
icon : 'fas fa-hat-wizard',
@@ -204,7 +212,7 @@ module.exports = [
}}
\n`;
},
- },
+ }
]
},
@@ -218,34 +226,51 @@ module.exports = [
view : 'text',
snippets : [
{
- name : 'Class Table',
- icon : 'fas fa-table',
- gen : ClassTableGen.full('classTable,frame,decoration,wide'),
- },
- {
- name : 'Class Table (unframed)',
- icon : 'fas fa-border-none',
- gen : ClassTableGen.full('classTable,wide'),
- },
- {
- name : '1/2 Class Table',
- icon : 'fas fa-list-alt',
- gen : ClassTableGen.half('classTable,decoration,frame'),
- },
- {
- name : '1/2 Class Table (unframed)',
- icon : 'fas fa-border-none',
- gen : ClassTableGen.half('classTable'),
- },
- {
- name : '1/3 Class Table',
- icon : 'fas fa-border-all',
- gen : ClassTableGen.third('classTable,frame'),
- },
- {
- name : '1/3 Class Table (unframed)',
- icon : 'fas fa-border-none',
- gen : ClassTableGen.third('classTable'),
+ name : 'Class Tables',
+ icon : 'fas fa-table',
+ gen : ClassTableGen.full('classTable,frame,decoration,wide'),
+ subsnippets : [
+ {
+ name : 'Martial Class Table',
+ icon : 'fas fa-table',
+ gen : ClassTableGen.non('classTable,frame,decoration'),
+ },
+ {
+ name : 'Martial Class Table (unframed)',
+ icon : 'fas fa-border-none',
+ gen : ClassTableGen.non('classTable'),
+ },
+ {
+ name : 'Full Caster Class Table',
+ icon : 'fas fa-table',
+ gen : ClassTableGen.full('classTable,frame,decoration,wide'),
+ },
+ {
+ name : 'Full Caster Class Table (unframed)',
+ icon : 'fas fa-border-none',
+ gen : ClassTableGen.full('classTable,wide'),
+ },
+ {
+ name : 'Half Caster Class Table',
+ icon : 'fas fa-list-alt',
+ gen : ClassTableGen.half('classTable,frame,decoration,wide'),
+ },
+ {
+ name : 'Half Caster Class Table (unframed)',
+ icon : 'fas fa-border-none',
+ gen : ClassTableGen.half('classTable,wide'),
+ },
+ {
+ name : 'Third Caster Spell Table',
+ icon : 'fas fa-border-all',
+ gen : ClassTableGen.third('classTable,frame,decoration'),
+ },
+ {
+ name : 'Third Caster Spell Table (unframed)',
+ icon : 'fas fa-border-none',
+ gen : ClassTableGen.third('classTable'),
+ }
+ ]
},
{
name : 'Rune Table',
diff --git a/themes/V3/5ePHB/snippets/classtable.gen.js b/themes/V3/5ePHB/snippets/classtable.gen.js
index c1f6254f9..1fdff036f 100644
--- a/themes/V3/5ePHB/snippets/classtable.gen.js
+++ b/themes/V3/5ePHB/snippets/classtable.gen.js
@@ -1,132 +1,138 @@
const _ = require('lodash');
+const dedent = require('dedent-tabs').default;
const features = [
- 'Astrological Botany',
- 'Biochemical Sorcery',
- 'Civil Divination',
- 'Consecrated Augury',
- 'Demonic Anthropology',
- 'Divinatory Mineralogy',
- 'Exo Interfacer',
- 'Genetic Banishing',
- 'Gunpowder Torturer',
- 'Gunslinger Corruptor',
- 'Hermetic Geography',
- 'Immunological Cultist',
- 'Malefic Chemist',
- 'Mathematical Pharmacy',
- 'Nuclear Biochemistry',
- 'Orbital Gravedigger',
- 'Pharmaceutical Outlaw',
- 'Phased Linguist',
- 'Plasma Gunslinger',
- 'Police Necromancer',
- 'Ritual Astronomy',
- 'Sixgun Poisoner',
- 'Seismological Alchemy',
- 'Spiritual Illusionism',
- 'Statistical Occultism',
- 'Spell Analyst',
- 'Torque Interfacer'
+ 'Astrological Botany', 'Biochemical Sorcery', 'Civil Divination',
+ 'Consecrated Augury', 'Demonic Anthropology', 'Divinatory Mineralogy',
+ 'Exo Interfacer', 'Genetic Banishing', 'Gunpowder Torturer',
+ 'Gunslinger Corruptor', 'Hermetic Geography', 'Immunological Cultist',
+ 'Malefic Chemist', 'Mathematical Pharmacy', 'Nuclear Biochemistry',
+ 'Orbital Gravedigger', 'Pharmaceutical Outlaw', 'Phased Linguist',
+ 'Plasma Gunslinger', 'Police Necromancer', 'Ritual Astronomy',
+ 'Sixgun Poisoner', 'Seismological Alchemy', 'Spiritual Illusionism',
+ 'Statistical Occultism', 'Spell Analyst', 'Torque Interfacer'
+].map((f)=>_.padEnd(f, 21)); // Pad to equal length of 21 chars long
+
+const classnames = [
+ 'Ackerman', 'Berserker-Typist', 'Concierge', 'Fishmonger',
+ 'Haberdasher', 'Manicurist', 'Netrunner', 'Weirkeeper'
];
-const classnames = ['Ackerman', 'Berserker-Typist', 'Concierge', 'Fishmonger',
- 'Haberdasher', 'Manicurist', 'Netrunner', 'Weirkeeper'];
-
-const levels = ['1st', '2nd', '3rd', '4th', '5th',
- '6th', '7th', '8th', '9th', '10th',
- '11th', '12th', '13th', '14th', '15th',
- '16th', '17th', '18th', '19th', '20th'];
-
-const profBonus = [2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6];
-
-const maxes = [4, 3, 3, 3, 3, 2, 2, 1, 1];
-
-const drawSlots = function(Slots, rows, padding){
- let slots = Number(Slots);
- return _.times(rows, function(i){
- const max = maxes[i];
- if(slots < 1) return _.pad('—', padding);
- const res = _.min([max, slots]);
- slots -= res;
- return _.pad(res.toString(), padding);
- }).join(' | ');
-};
-
module.exports = {
- full : function(classes){
- const classname = _.sample(classnames);
-
-
- let cantrips = 3;
- let spells = 1;
- let slots = 2;
- return `{{${classes}\n##### The ${classname}\n` +
- `| Level | Proficiency | Features | Cantrips | Spells | --- Spell Slots Per Spell Level ---|||||||||\n`+
- `| ^| Bonus ^| ^| Known ^| Known ^|1st |2nd |3rd |4th |5th |6th |7th |8th |9th |\n`+
- `|:-----:|:-----------:|:-------------|:--------:|:------:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|\n${
- _.map(levels, function(levelName, level){
- const res = [
- _.pad(levelName, 5),
- _.pad(`+${profBonus[level]}`, 2),
- _.padEnd(_.sample(features), 21),
- _.pad(cantrips.toString(), 8),
- _.pad(spells.toString(), 6),
- drawSlots(slots, 9, 2),
- ].join(' | ');
-
- cantrips += _.random(0, 1);
- spells += _.random(0, 1);
- slots += _.random(0, 2);
-
- return `| ${res} |`;
- }).join('\n')}\n}}\n\n`;
+ non : function(snippetClasses){
+ return dedent`
+ {{${snippetClasses}
+ ##### The ${_.sample(classnames)}
+ | Level | Proficiency Bonus | Features | ${_.sample(features)} |
+ |:-----:|:-----------------:|:---------|:---------------------:|
+ | 1st | +2 | ${_.sample(features)} | 2 |
+ | 2nd | +2 | ${_.sample(features)} | 2 |
+ | 3rd | +2 | ${_.sample(features)} | 3 |
+ | 4th | +2 | ${_.sample(features)} | 3 |
+ | 5th | +3 | ${_.sample(features)} | 3 |
+ | 6th | +3 | ${_.sample(features)} | 4 |
+ | 7th | +3 | ${_.sample(features)} | 4 |
+ | 8th | +3 | ${_.sample(features)} | 4 |
+ | 9th | +4 | ${_.sample(features)} | 4 |
+ | 10th | +4 | ${_.sample(features)} | 4 |
+ | 11th | +4 | ${_.sample(features)} | 4 |
+ | 12th | +4 | ${_.sample(features)} | 5 |
+ | 13th | +5 | ${_.sample(features)} | 5 |
+ | 14th | +5 | ${_.sample(features)} | 5 |
+ | 15th | +5 | ${_.sample(features)} | 5 |
+ | 16th | +5 | ${_.sample(features)} | 5 |
+ | 17th | +6 | ${_.sample(features)} | 6 |
+ | 18th | +6 | ${_.sample(features)} | 6 |
+ | 19th | +6 | ${_.sample(features)} | 6 |
+ | 20th | +6 | ${_.sample(features)} | unlimited |
+ }}\n\n`;
},
- half : function(classes){
- const classname = _.sample(classnames);
-
- let featureScore = 1;
- return `{{${classes}\n##### The ${classname}\n` +
- `| Level | Proficiency Bonus | Features | ${_.pad(_.sample(features), 21)} |\n` +
- `|:-----:|:-----------------:|:---------|:---------------------:|\n${
- _.map(levels, function(levelName, level){
- const res = [
- _.pad(levelName, 5),
- _.pad(`+${profBonus[level]}`, 2),
- _.padEnd(_.sample(features), 23),
- _.pad(`+${featureScore}`, 21),
- ].join(' | ');
-
- featureScore += _.random(0, 1);
-
- return `| ${res} |`;
- }).join('\n')}\n}}\n\n`;
+ full : function(snippetClasses){
+ return dedent`
+ {{${snippetClasses}
+ ##### The ${_.sample(classnames)}
+ | Level | Proficiency | Features | Cantrips | --- Spell Slots Per Spell Level ---|||||||||
+ | ^| Bonus ^| ^| Known ^|1st |2nd |3rd |4th |5th |6th |7th |8th |9th |
+ |:-----:|:-----------:|:-------------|:--------:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|
+ | 1st | +2 | ${_.sample(features)} | 2 | 2 | — | — | — | — | — | — | — | — |
+ | 2nd | +2 | ${_.sample(features)} | 2 | 3 | — | — | — | — | — | — | — | — |
+ | 3rd | +2 | ${_.sample(features)} | 2 | 4 | 2 | — | — | — | — | — | — | — |
+ | 4th | +2 | ${_.sample(features)} | 3 | 4 | 3 | — | — | — | — | — | — | — |
+ | 5th | +3 | ${_.sample(features)} | 3 | 4 | 3 | 2 | — | — | — | — | — | — |
+ | 6th | +3 | ${_.sample(features)} | 3 | 4 | 3 | 3 | — | — | — | — | — | — |
+ | 7th | +3 | ${_.sample(features)} | 3 | 4 | 3 | 3 | 1 | — | — | — | — | — |
+ | 8th | +3 | ${_.sample(features)} | 3 | 4 | 3 | 3 | 2 | — | — | — | — | — |
+ | 9th | +4 | ${_.sample(features)} | 3 | 4 | 3 | 3 | 2 | 1 | — | — | — | — |
+ | 10th | +4 | ${_.sample(features)} | 3 | 4 | 3 | 3 | 2 | 1 | — | — | — | — |
+ | 11th | +4 | ${_.sample(features)} | 4 | 4 | 3 | 3 | 2 | 1 | 1 | — | — | — |
+ | 12th | +4 | ${_.sample(features)} | 4 | 4 | 3 | 3 | 2 | 1 | 1 | — | — | — |
+ | 13th | +5 | ${_.sample(features)} | 4 | 4 | 3 | 3 | 2 | 1 | 1 | 1 | — | — |
+ | 14th | +5 | ${_.sample(features)} | 4 | 4 | 3 | 3 | 2 | 1 | 1 | 1 | — | — |
+ | 15th | +5 | ${_.sample(features)} | 4 | 4 | 3 | 3 | 2 | 1 | 1 | 1 | 1 | — |
+ | 16th | +5 | ${_.sample(features)} | 4 | 4 | 3 | 3 | 2 | 1 | 1 | 1 | 1 | — |
+ | 17th | +6 | ${_.sample(features)} | 4 | 4 | 3 | 3 | 2 | 1 | 1 | 1 | 1 | 1 |
+ | 18th | +6 | ${_.sample(features)} | 4 | 4 | 3 | 3 | 3 | 1 | 1 | 1 | 1 | 1 |
+ | 19th | +6 | ${_.sample(features)} | 4 | 4 | 3 | 3 | 3 | 2 | 2 | 1 | 1 | 1 |
+ | 20th | +6 | ${_.sample(features)} | 4 | 4 | 3 | 3 | 3 | 2 | 2 | 2 | 1 | 1 |
+ }}\n\n`;
},
- third : function(classes){
- const classname = _.sample(classnames);
+ half : function(snippetClasses){
+ return dedent`
+ {{${snippetClasses}
+ ##### The ${_.sample(classnames)}
+ | Level | Proficiency | Features | Spells |--- Spell Slots Per Spell Level ---|||||
+ | ^| Bonus ^| ^| Known ^| 1st | 2nd | 3rd | 4th | 5th |
+ |:-----:|:-----------:|:-------------|:------:|:-----:|:-----:|:-----:|:-----:|:-----:|
+ | 1st | +2 | ${_.sample(features)} | — | — | — | — | — | — |
+ | 2nd | +2 | ${_.sample(features)} | 2 | 2 | — | — | — | — |
+ | 3rd | +2 | ${_.sample(features)} | 3 | 3 | — | — | — | — |
+ | 4th | +2 | ${_.sample(features)} | 3 | 3 | — | — | — | — |
+ | 5th | +3 | ${_.sample(features)} | 4 | 4 | 2 | — | — | — |
+ | 6th | +3 | ${_.sample(features)} | 4 | 4 | 2 | — | — | — |
+ | 7th | +3 | ${_.sample(features)} | 5 | 4 | 3 | — | — | — |
+ | 8th | +3 | ${_.sample(features)} | 5 | 4 | 3 | — | — | — |
+ | 9th | +4 | ${_.sample(features)} | 6 | 4 | 3 | 2 | — | — |
+ | 10th | +4 | ${_.sample(features)} | 6 | 4 | 3 | 2 | — | — |
+ | 11th | +4 | ${_.sample(features)} | 7 | 4 | 3 | 3 | — | — |
+ | 12th | +4 | ${_.sample(features)} | 7 | 4 | 3 | 3 | — | — |
+ | 13th | +5 | ${_.sample(features)} | 8 | 4 | 3 | 3 | 1 | — |
+ | 14th | +5 | ${_.sample(features)} | 8 | 4 | 3 | 3 | 1 | — |
+ | 15th | +5 | ${_.sample(features)} | 9 | 4 | 3 | 3 | 2 | — |
+ | 16th | +5 | ${_.sample(features)} | 9 | 4 | 3 | 3 | 2 | — |
+ | 17th | +6 | ${_.sample(features)} | 10 | 4 | 3 | 3 | 3 | 1 |
+ | 18th | +6 | ${_.sample(features)} | 10 | 4 | 3 | 3 | 3 | 1 |
+ | 19th | +6 | ${_.sample(features)} | 11 | 4 | 3 | 3 | 3 | 2 |
+ | 20th | +6 | ${_.sample(features)} | 11 | 4 | 3 | 3 | 3 | 2 |
+ }}\n\n`;
+ },
- let cantrips = 3;
- let spells = 1;
- let slots = 2;
- return `{{${classes}\n##### ${classname} Spellcasting\n` +
- `| Class | Cantrips | Spells |--- Spells Slots per Spell Level ---||||\n` +
- `| Level ^| Known ^| Known ^| 1st | 2nd | 3rd | 4th |\n` +
- `|:------:|:--------:|:-------:|:-------:|:-------:|:-------:|:-------:|\n${
- _.map(levels, function(levelName, level){
- const res = [
- _.pad(levelName, 6),
- _.pad(cantrips.toString(), 8),
- _.pad(spells.toString(), 7),
- drawSlots(slots, 4, 7),
- ].join(' | ');
-
- cantrips += _.random(0, 1);
- spells += _.random(0, 1);
- slots += _.random(0, 1);
-
- return `| ${res} |`;
- }).join('\n')}\n}}\n\n`;
+ third : function(snippetClasses){
+ return dedent`
+ {{${snippetClasses}
+ ##### ${_.sample(classnames)} Spellcasting
+ | Level | Cantrips | Spells |--- Spells Slots per Spell Level ---||||
+ | ^| Known ^| Known ^| 1st | 2nd | 3rd | 4th |
+ |:-----:|:--------:|:------:|:-------:|:-------:|:-------:|:-------:|
+ | 3rd | 2 | 3 | 2 | — | — | — |
+ | 4th | 2 | 4 | 3 | — | — | — |
+ | 5th | 2 | 4 | 3 | — | — | — |
+ | 6th | 2 | 4 | 3 | — | — | — |
+ | 7th | 2 | 5 | 4 | 2 | — | — |
+ | 8th | 2 | 6 | 4 | 2 | — | — |
+ | 9th | 2 | 6 | 4 | 2 | — | — |
+ | 10th | 3 | 7 | 4 | 3 | — | — |
+ | 11th | 3 | 8 | 4 | 3 | — | — |
+ | 12th | 3 | 8 | 4 | 3 | — | — |
+ | 13th | 3 | 9 | 4 | 3 | 2 | — |
+ | 14th | 3 | 10 | 4 | 3 | 2 | — |
+ | 15th | 3 | 10 | 4 | 3 | 2 | — |
+ | 16th | 3 | 11 | 4 | 3 | 3 | — |
+ | 17th | 3 | 11 | 4 | 3 | 3 | — |
+ | 18th | 3 | 11 | 4 | 3 | 3 | — |
+ | 19th | 3 | 12 | 4 | 3 | 3 | 1 |
+ | 20th | 3 | 13 | 4 | 3 | 3 | 1 |
+ }}\n\n`;
}
};
diff --git a/themes/V3/5ePHB/snippets/coverpage.gen.js b/themes/V3/5ePHB/snippets/coverpage.gen.js
index bd52e1d9e..865269f92 100644
--- a/themes/V3/5ePHB/snippets/coverpage.gen.js
+++ b/themes/V3/5ePHB/snippets/coverpage.gen.js
@@ -68,13 +68,23 @@ const footnote = [
'In an amazing kingdom, in an age of sorcery and lost souls, eight space pirates quest for freedom.'
];
+const coverText = [
+ 'Embark on a thrilling journey across a vast and varied world, where magic and mystery await you at every turn. Encounter strange creatures and ancient secrets, and forge your own destiny with your choices. The world is yours to shape and explore.',
+ 'Join a band of brave adventurers and set out to explore the unknown lands beyond the horizon. Along the way, you’ll face perilous challenges, make new friends and enemies, and uncover a plot that threatens to destroy everything you hold dear. The fate of the world rests in your hands.',
+ 'Create your own character and enter a realm of endless possibilities, where you can be whoever you want to be. Whether you prefer to fight, sneak, charm, or craft your way through the game, you’ll find a style that suits you. The only limit is your imagination.',
+ 'Experience a rich and immersive story that adapts to your actions and decisions. Every choice you make has consequences, for good or ill. Will you be a hero or a villain? A leader or a follower? A friend or a foe? The choice is yours.',
+ 'Dive into a world of epic fantasy and adventure, where you can explore ancient civilizations, dark dungeons, and hidden secrets. Along the way, you’ll meet colorful characters, collect powerful items, and learn new skills. The more you play, the more you’ll discover.',
+ 'Explore a vast and dynamic world that changes according to your actions. You can shape the environment, influence the politics, and alter the history of the game world. But be careful, as every change has a ripple effect that may have unforeseen consequences.',
+ 'Enter a world of wonder and danger, where you can find allies and enemies among the various races and factions that inhabit it. You can choose to join or oppose any of them, or forge your own path. The game world is alive and responsive to your actions.'
+];
+
module.exports = {
front : function() {
return dedent`
{{frontCover}}
- {{logo }}
+ {{logo }}
# ${_.sample(titles)}
## ${_.sample(subtitles)}
@@ -103,7 +113,7 @@ module.exports = {
{position:absolute,bottom:0,left:0,height:100%}
}}
- {{logo }}
+ {{logo }}
\page`;
},
@@ -119,6 +129,28 @@ module.exports = {
{position:absolute,bottom:0,left:0,height:100%}
}}
+ \page`;
+ },
+
+ back : function() {
+ return dedent`
+ {{backCover}}
+
+ # ${_.sample(subtitles)}
+
+ ${_.sampleSize(coverText, 3).join('\n:\n')}
+ ___
+
+ For use with any fantasy roleplaying ruleset. Play the best game of your life!
+
+ {position:absolute,bottom:0,left:0,height:100%}
+
+ {{logo
+ 
+
+ Homebrewery.Naturalcrit.com
+ }}
+
\page`;
}
};
diff --git a/themes/V3/5ePHB/snippets/index.gen.js b/themes/V3/5ePHB/snippets/index.gen.js
new file mode 100644
index 000000000..8de5df14c
--- /dev/null
+++ b/themes/V3/5ePHB/snippets/index.gen.js
@@ -0,0 +1,85 @@
+const dedent = require('dedent-tabs').default;
+
+module.exports = ()=>{
+ return dedent`
+ {{index,wide,columns:5;
+ ##### Index
+ - Ankhesh-Bort
+ - city map, 7
+ - city watch, 12
+ - guilds, 19
+ - Cheese
+ - types of cheese, 8
+ - cheese-related magic, 14
+ - cheese-related quests, 26-27
+ - Death
+ - appearance, 10
+ - personality, 13
+ - hobbies, 23
+ - Elves
+ - types of elves, 15
+ - elvish magic, 24
+ - elvish curses, 28
+ - Footnotes
+ - types of footnotes, 16-17
+ - footnote rules, 20-21
+ - footnote humor, 29-30
+ - Gods
+ - types of gods, 12
+ - godly interventions, 25
+ - godly conflicts, 31
+ - Heroes
+ - class features, 11-12
+ - heroic deeds, 26-27
+ - Inns
+ - types of inns, 9
+ - inn amenities, 18
+ - Jokes
+ - types of jokes, 11-12
+ - joke delivery, 25
+ - Knives
+ - types of knives, 16-17
+ - knife skills, 22-23
+ - knife fights, 28-29
+ - Luggage
+ - appearance, 10
+ - personality, 13
+ - abilities, 23
+ - Magic
+ - types of magic, 15
+ - magic rules, 24
+ - magic mishaps, 28
+ - Socks
+ - types of socks, 9
+ - sock-related magic (yes, really), 15
+ - sock-related quests (no, really), 26
+ - Trolls
+ - appearance and biology, 11
+ - culture and language, 18
+ - troll rights and activism, 31
+ - Unknown University
+ - history and architecture, 12
+ - faculty and staff, 20
+ - courses and exams, 33
+ - Vampires
+ - types and origins, 13
+ - vampiric powers and weaknesses, 21
+ - vampiric etiquette and politics, 34
+ - Witches
+ - types and traditions, 14
+ - witchcraft and headology, 22
+ - witch trials and tribulations, 35
+ - Xylophones
+ - musical instruments or weapons?, 15
+ - xylophone-related magic and lore, 23
+ - xylophone-related quests and puzzles, 36
+ - Yetis
+ - appearance and behavior, 16
+ - yeti philosophy and religion, 24
+ - yeti encounters and stories, 37
+ - Zombies
+ - types and causes, 17
+ - zombie rights and duties, 25
+ - zombie survival and prevention, 38
+ }}`;
+};
\ No newline at end of file
diff --git a/themes/V3/5ePHB/snippets/partcoverpage.gen.js b/themes/V3/5ePHB/snippets/partcoverpage.gen.js
deleted file mode 100644
index 5b808aabb..000000000
--- a/themes/V3/5ePHB/snippets/partcoverpage.gen.js
+++ /dev/null
@@ -1,50 +0,0 @@
-const _ = require('lodash');
-
-var titles = [
- 'Introduction to the World of DnD',
- 'Creating Your Character',
- 'The Rules of the Game',
- 'Combat and Combat Strategies',
- 'Magic and Spellcasting',
- 'Adventuring and Exploration',
- 'Dungeon Delving',
- 'Campaign Building and World Building',
- 'DM Techniques and Tips',
- 'Appendix: Reference Material',
- 'Monsters and Creatures',
- 'Equipment and Treasure',
- 'Non-Player Characters (NPCs)',
- 'Experience and Leveling',
- 'Races and Classes',
- 'Skills and Abilities',
- 'Alignment and Moral Choices',
- 'Player-vs-Player Conflict',
- 'Game Mastering 101',
- 'Running a Successful Campaign',
- 'Worldbuilding and Lore',
- 'Designing Encounters and Adventures',
- 'Managing Players and their Expectations',
- 'Factions and Political Intrigue',
- 'Adventure Hooks and Plot Ideas',
- 'Building a Campaign Setting',
- 'Handling Rules Disputes',
- 'Running Large-Scale Battles',
- 'Designing Unique Magic Systems',
- 'Developing and Using NPCs',
- 'Crafting Memorable Quests',
- 'Improvising When Things Don\'t Go as Planned',
- 'Managing Session Flow and Pacing',
- 'Building a World That Feels Alive'
-];
-
-module.exports = ()=>{
- return `{{partCover}}
-
-# PART X
-## ${_.sample(titles)}
-
-{{imageMaskEdge5,--offset:10cm,--rotation:180
-{height:100%}
-}}
-\\page`;
-};
diff --git a/themes/V3/5ePHB/snippets/quote.gen.js b/themes/V3/5ePHB/snippets/quote.gen.js
new file mode 100644
index 000000000..c5e3d6293
--- /dev/null
+++ b/themes/V3/5ePHB/snippets/quote.gen.js
@@ -0,0 +1,51 @@
+const _ = require("lodash");
+
+const quotes = [
+ "The sword glinted in the dim light, its edges keen and deadly. As the adventurer reached for it, he couldn't help but feel a surge of excitement mixed with fear. This was no ordinary blade.",
+ "The dragon's roar shook the ground beneath their feet, and the brave knight stood tall, his sword at the ready. He knew that this would be the battle of his life, but he was determined to emerge victorious.",
+ "The wizard's laboratory was a sight to behold, filled with bubbling cauldrons, ancient tomes, and strange artifacts from distant lands. As the apprentice gazed around in wonder, she knew that she was about to embark on a journey unlike any other.",
+ "The tavern was packed with rowdy patrons, their voices raised in song and laughter. The bard took center stage, strumming his lute and launching into a tale of adventure and heroism that had the crowd hanging on his every word.",
+ "The thief crept through the shadows, his eyes scanning the room for any sign of danger. He knew that one false move could mean the difference between success and failure, and he was determined to come out on top.",
+ "The elf queen stood atop her castle walls, surveying the kingdom below with a mix of pride and sadness. She knew that the coming war would be brutal, but she was determined to protect her people at all costs.",
+ "The necromancer's tower loomed in the distance, its dark spires piercing the sky. As the adventurers approached, they could feel the chill of death emanating from within",
+ "The ranger moved through the forest like a shadow, his senses attuned to every sound and movement around him. He knew that danger lurked behind every tree, but he was ready for whatever came his way.",
+ "The paladin knelt before the altar, his hands clasped in prayer. He knew that his faith would be tested in the days ahead, but he was ready to face whatever trials lay in store for him.",
+ "The druid communed with the spirits of nature, his mind merging with the trees, the animals, and the very earth itself. He knew that his power came with a great responsibility, and he was determined to use it for the greater good.",
+];
+
+const authors = [
+ "Unknown",
+ "James Wyatt",
+ "Eolande Blackwood",
+ "Ragnar Ironheart",
+ "Lyra Nightshade",
+ "Valtorius Darkstar",
+ "Isadora Fireheart",
+ "Theron Shadowbane",
+ "Lirien Starweaver",
+ "Drogathar Bonecrusher",
+ "Kaelen Frostblade",
+];
+
+const books = [
+ "The Blade of Destiny",
+ "Dragonfire and Steel",
+ "The Bard's Tale",
+ "Darkness Rising",
+ "The Sacred Quest",
+ "Shadows in the Forest",
+ "The Starweaver Chronicles",
+ "Beneath the Bones",
+ "Moonlit Magic",
+ "Frost and Fury",
+
+];
+module.exports = () => {
+ return `
+{{quote
+${_.sample(quotes)}
+
+{{attribution ${_.sample(authors)}, *${_.sample(books)}*}}
+}}
+\n`;
+};
diff --git a/themes/V3/5ePHB/snippets/tableOfContents.gen.js b/themes/V3/5ePHB/snippets/tableOfContents.gen.js
index 1c52d5cf7..97d82ed40 100644
--- a/themes/V3/5ePHB/snippets/tableOfContents.gen.js
+++ b/themes/V3/5ePHB/snippets/tableOfContents.gen.js
@@ -29,27 +29,29 @@ const getTOC = (pages)=>{
const res = [];
_.each(pages, (page, pageNum)=>{
- const lines = page.split('\n');
- _.each(lines, (line)=>{
- if(_.startsWith(line, '# ')){
- const title = line.replace('# ', '');
- add1(title, pageNum);
- }
- if(_.startsWith(line, '## ')){
- const title = line.replace('## ', '');
- add2(title, pageNum);
- }
- if(_.startsWith(line, '### ')){
- const title = line.replace('### ', '');
- add3(title, pageNum);
- }
- });
+ if(!page.includes("{{frontCover}}") && !page.includes("{{insideCover}}") && !page.includes("{{partCover}}") && !page.includes("{{backCover}}")) {
+ const lines = page.split('\n');
+ _.each(lines, (line)=>{
+ if(_.startsWith(line, '# ')){
+ const title = line.replace('# ', '');
+ add1(title, pageNum);
+ }
+ if(_.startsWith(line, '## ')){
+ const title = line.replace('## ', '');
+ add2(title, pageNum);
+ }
+ if(_.startsWith(line, '### ')){
+ const title = line.replace('### ', '');
+ add3(title, pageNum);
+ }
+ });
+ }
});
return res;
};
-module.exports = function(brew){
- const pages = brew.text.split('\\page');
+module.exports = function(props){
+ const pages = props.brew.text.split('\\page');
const TOC = getTOC(pages);
const markdown = _.reduce(TOC, (r, g1, idx1)=>{
if(g1.title !== null) {
diff --git a/themes/V3/5ePHB/style.less b/themes/V3/5ePHB/style.less
index e5be33ad1..f37dfae2b 100644
--- a/themes/V3/5ePHB/style.less
+++ b/themes/V3/5ePHB/style.less
@@ -14,35 +14,23 @@
--HB_Color_Footnotes : #C9AD6A; // Gold
}
-@page { margin: 0; }
-body {
- counter-reset : phb-page-numbers;
-}
-*{
- -webkit-print-color-adjust : exact;
-}
-.useSansSerif(){
- font-family : ScalySansRemake;
+@page { margin : 0; }
+body { counter-reset : phb-page-numbers; }
+* { -webkit-print-color-adjust : exact; }
+.useSansSerif() {
+ font-family : "ScalySansRemake";
font-size : 0.318cm;
line-height : 1.2em;
- p,dl,ul,ol {
- line-height : 1.2em;
- }
- ul, ol {
- padding-left : 1em;
- }
- em{
- font-style : italic;
- }
- strong{
+ p,dl,ul,ol { line-height : 1.2em; }
+ ul, ol { padding-left : 1em; }
+ em { font-style : italic; }
+ strong {
font-weight : 800;
letter-spacing : -0.02em;
}
- h5 + * {
- margin-top : 0.1cm;
- }
+ h5 + * { margin-top : 0.1cm; }
}
-.useColumns(@multiplier : 1, @fillMode: balance){
+.useColumns(@multiplier : 1, @fillMode: auto) {
column-count : 2;
column-fill : @fillMode;
column-gap : 0.9cm;
@@ -54,252 +42,254 @@ body {
-webkit-column-gap : 0.9cm;
-moz-column-gap : 0.9cm;
}
-.columnWrapper{
+.columnWrapper {
+ column-gap : inherit;
max-height : 100%;
column-span : all;
columns : inherit;
- column-gap : inherit;
}
-.page{
+.page {
.useColumns();
- counter-increment : phb-page-numbers;
position : relative;
z-index : 15;
box-sizing : border-box;
- overflow : hidden;
- height : 279.4mm;
width : 215.9mm;
+ height : 279.4mm;
+ padding : 1.4cm 1.9cm 1.7cm;
+ overflow : hidden;
+ font-family : "BookInsanityRemake";
+ font-size : 0.34cm;
+ counter-increment : phb-page-numbers;
background-color : var(--HB_Color_Background);
background-image : @backgroundImage;
- padding : 1.4cm 1.9cm 1.7cm;
- font-family : BookInsanityRemake;
- font-size : 0.34cm;
text-rendering : optimizeLegibility;
page-break-before : always;
page-break-after : always;
}
- //*****************************
- // * BASE
+//*****************************
+// * BASE
// *****************************/
-.page{
- p{
- overflow-wrap : break-word; //TODO: MAKE ALL MARGINS TOP-ONLY. USE * + * STYLE SELECTORS
+.page {
+ p {
display : block;
line-height : 1.25em;
- &+* {
- margin-top : 0.325cm;
- }
- &+p{
- margin-top : 0;
- }
+ overflow-wrap : break-word; //TODO: MAKE ALL MARGINS TOP-ONLY. USE * + * STYLE SELECTORS
+ & + * { margin-top : 0.325cm; }
+ & + p { margin-top : 0; }
}
- ul{
- margin-bottom : 0.8em;
+ ul {
padding-left : 1.4em;
+ margin-bottom : 0.8em;
line-height : 1.25em;
list-style-position : outside;
list-style-type : disc;
}
- ol{
- margin-bottom : 0.8em;
+ ol {
padding-left : 1.4em;
+ margin-bottom : 0.8em;
line-height : 1.25em;
list-style-position : outside;
list-style-type : decimal;
}
//Indents after p or lists
- p+p, ul+p, ol+p{
- text-indent : 1em;
- }
- img{
- z-index : -1;
- }
- strong{
+ p + p, ul + p, ol + p { text-indent : 1em; }
+ img { z-index : -1; }
+ strong {
font-weight : bold;
letter-spacing : -0.02em;
}
- em{
- font-style : italic;
- }
- sup{
+ em { font-style : italic; }
+ sup {
+ font-size : smaller;
+ line-height : 0;
vertical-align : super;
- font-size : smaller;
- line-height : 0;
}
- sub{
- vertical-align : sub;
+ sub {
font-size : smaller;
line-height : 0;
+ vertical-align : sub;
}
//*****************************
// * HEADERS
// *****************************/
- h1,h2,h3,h4{
- font-family : MrEavesRemake;
+ h1,h2,h3,h4 {
+ font-family : "MrEavesRemake";
font-weight : 800;
color : var(--HB_Color_HeaderText);
}
- h1{
+ h1 {
margin-bottom : 0.18cm; //Margin-bottom only because this is WIDE
column-span : all;
font-size : 0.89cm;
line-height : 1em;
-webkit-column-span : all;
-moz-column-span : all;
- &+p::first-letter{
+ & + p::first-letter {
float : left;
- font-family : SolberaImitationRemake;
- line-height : 1em;
- font-size : 3.5cm;
- padding-left : 40px; //Allow background color to extend into margins
- margin-left : -40px;
- margin-top : -0.3cm;
padding-bottom : 2px;
+ padding-left : 40px; //Allow background color to extend into margins
+ margin-top : -0.3cm;
margin-bottom : -20px;
- background-image : linear-gradient(-45deg, #322814, #998250, #322814);
- background-clip : text;
- -webkit-background-clip : text;
+ margin-left : -40px;
+ font-family : "SolberaImitationRemake";
+ font-size : 3.5cm;
+ line-height : 1em;
color : rgba(0, 0, 0, 0);
+ background-image : linear-gradient(-45deg, #322814, #998250, #322814);
+ -webkit-background-clip : text;
+ background-clip : text;
}
- &+p::first-line{
- font-variant : small-caps;
- }
+ & + p::first-line { font-variant : small-caps; }
}
- h2{
+ h2 {
//margin-top : 0px; //Font is misaligned. Shift up slightly
//margin-bottom : 0.05cm;
font-size : 0.75cm;
line-height : 0.988em; //Font is misaligned. Shift up slightly
}
- h3{
+ h3 {
//margin-top : -0.1cm; //Font is misaligned. Shift up slightly
//margin-bottom : 0.1cm;
- font-size : 0.575cm;
- border-bottom : 2px solid var(--HB_Color_HeaderUnderline);;
+ font-size : 0.575cm;;
line-height : 0.995em; //Font is misaligned. Shift up slightly
- & + * {
- margin-top: 0.17cm;
- }
+ border-bottom : 2px solid var(--HB_Color_HeaderUnderline);
+ & + * { margin-top : 0.17cm; }
}
* + h3 {
margin-top : 0.155cm; //(0.325 - 0.17)
}
- h4{
+ h4 {
//margin-top : -0.02cm; //Font is misaligned. Shift up slightly
//margin-bottom : 0.02cm;
font-size : 0.458cm;
line-height : 0.971em; //Font is misaligned. Shift up slightly
- & + * {
- margin-top: 0.09cm;
- }
+ & + * { margin-top : 0.09cm; }
}
* + h4 {
margin-top : 0.235cm; //(0.325 - 0.09)
}
- h5{
+ h5 {
//margin-top : -0.02cm; //Font is misaligned. Shift up slightly
//margin-bottom : 0.02cm;
- font-family : ScalySansSmallCapsRemake;
+ font-family : "ScalySansSmallCapsRemake";
font-size : 0.423cm;
font-weight : 900;
line-height : 0.951em; //Font is misaligned. Shift up slightly
- & + * {
- margin-top : 0.2cm;
- }
+ & + * { margin-top : 0.2cm; }
}
//*****************************
// * TABLE
// *****************************/
- table{
+ table {
.useSansSerif();
width : 100%;
line-height : 16px;
- & + * {
- margin-top : 0.325cm;
- }
- thead{
- display: table-row-group;
+ & + * { margin-top : 0.325cm; }
+ thead {
+ display : table-row-group;
font-weight : 800;
- th{
- vertical-align : bottom;
+ th {
//padding : 0.14em 0.4em;
- padding : 0px 1.5px; // Both of these are temporary, just to force
+ padding : 0px 1.5px; // Both of these are temporary, just to force
+ vertical-align : bottom;
//line-height : 16px; // PDF to render at same height until Chrome 108
}
}
- tbody{
- tr{
- td{
+ tbody {
+ tr {
+ td {
//padding : 0.14em 0.4em;
padding : 0px 1.5px; // Both of these are temporary, just to force
- //line-height : 16px; // PDF to render at same height until Chrome 108
- }
- &:nth-child(odd){
- background-color : var(--HB_Color_Accent);
+ //line-height : 16px; // PDF to render at same height until Chrome 108
}
+ &:nth-child(odd) { background-color : var(--HB_Color_Accent); }
}
}
}
+ //*****************************
+ // * QUOTE
+ // *****************************/
+ .quote {
+
+ & > p {
+ font-style : italic;
+ line-height : 0.54cm;
+
+ &:first-child::first-line {
+ font-size : 0.38cm;
+ font-style : normal;
+ font-variant : small-caps;
+ }
+ }
+
+ p + .attribution { margin-top : 0; }
+
+ .attribution {
+ display : block;
+ font-style : normal;
+ line-height : 0.54cm;
+ text-align : right;
+
+ &::before {
+ margin-right : 0.2em;
+ content : '---';
+ }
+ }
+
+ & + * { margin-top : 0.54cm; }
+
+ }
+
+
+
//*****************************
// * NOTE
// *****************************/
- .note{
+ .note {
.useSansSerif();
+ padding : 0.13cm 0.16cm;
background-color : var(--HB_Color_Accent);
border-style : solid;
border-width : 1px;
border-image : @noteBorderImage 12 stretch;
- border-image-outset : 9px 0px;
border-image-width : 11px;
- padding : 0.13cm 0.16cm;
- box-shadow : 1px 4px 14px #888;
+ border-image-outset : 9px 0px;
+ box-shadow : 1px 4px 14px #888888;
.page :where(&) {
margin-top : 9px; //Prevent top border getting cut off on colbreak
}
- & + * {
- margin-top : 0.45cm;
- }
- h5 {
- font-size : 0.375cm;
- }
- p{
+ & + * { margin-top : 0.45cm; }
+ h5 { font-size : 0.375cm; }
+ p {
display : block;
padding-bottom : 0px;
}
- :last-child {
- margin-bottom : 0;
- }
+ :last-child { margin-bottom : 0; }
}
//************************************
// * DESCRIPTIVE TEXT BOX
// ************************************/
- .descriptive{
+ .descriptive {
.useSansSerif();
- background-color : #faf7ea;
+ padding : 0.1em;
+ background-color : #FAF7EA;
border-style : solid;
border-width : 7px;
border-image : @descriptiveBoxImage 12 stretch;
border-image-outset : 4px;
- padding : 0.1em;
- box-shadow : 0 0 6px #faf7ea;
+ box-shadow : 0 0 6px #FAF7EA;
.page :where(&) {
margin-top : 4px; //Prevent top border getting cut off on colbreak
}
- & + * {
- margin-top : 0.45cm;
- }
- h5 {
- font-size : 0.375cm;
- }
- p{
+ & + * { margin-top : 0.45cm; }
+ h5 { font-size : 0.375cm; }
+ p {
display : block;
padding-bottom : 0px;
line-height : 1.5em;
}
- :last-child {
- margin-bottom : 0;
- }
+ :last-child { margin-bottom : 0; }
}
//*****************************
// * Images Snippets
@@ -309,76 +299,72 @@ body {
.artist {
position : absolute;
width : auto;
- text-align : center;
- font-family : WalterTurncoat;
+ font-family : "WalterTurncoat";
font-size : 0.27cm;
color : var(--HB_Color_CaptionText);
+ text-align : center;
p, p + p {
margin : unset;
- text-indent : unset;
line-height : 1em;
+ text-indent : unset;
}
- h5 {
+ h5 {
+ font-family : "WalterTurncoat";
font-size : 1.3em;
- font-family : WalterTurncoat;
}
- a{
+ a {
color : inherit;
text-decoration : unset;
- &:hover {
- text-decoration : underline;
- }
+ &:hover { text-decoration : underline; }
}
}
/* Watermark */
.watermark {
- display : grid !important;
- place-items : center;
- justify-content : center;
position : absolute;
top : 0;
left : 0;
+ z-index : 500;
+ display : grid !important;
+ place-items : center;
+ justify-content : center;
width : 100%;
height : 100%;
font-size : 120px;
- text-transform : uppercase;
color : black;
+ text-transform : uppercase;
mix-blend-mode : overlay;
opacity : 30%;
transform : rotate(-45deg);
- z-index : 500;
- p {
- margin-bottom : none;
- }
+ p { margin-bottom : none; }
}
/* Watercolor */
- [class*="watercolor"] {
+ [class*='watercolor'] {
position : absolute;
+ z-index : -2;
width : 2000px; /* dimensions need to be real big so the user can set */
height : 2000px; /* height or width and the image will maintain aspect ratio */
+ background-color : var(--HB_Color_WatercolorStain); /* default color */
+ background-size : cover;
-webkit-mask-image : var(--wc);
-webkit-mask-size : contain;
-webkit-mask-repeat : no-repeat;
mask-image : var(--wc);
mask-size : contain;
mask-repeat : no-repeat;
- background-size : cover;
- background-color : var(--HB_Color_WatercolorStain); /*default color*/
- --wc : @watercolor1; /*default image*/
- z-index : -2;
+ --wc : @watercolor1; /* default image */
}
- .watercolor1 { --wc : @watercolor1; }
- .watercolor2 { --wc : @watercolor2; }
- .watercolor3 { --wc : @watercolor3; }
- .watercolor4 { --wc : @watercolor4; }
- .watercolor5 { --wc : @watercolor5; }
- .watercolor6 { --wc : @watercolor6; }
- .watercolor7 { --wc : @watercolor7; }
- .watercolor8 { --wc : @watercolor8; }
- .watercolor9 { --wc : @watercolor9; }
+ .watercolor1 { --wc : @watercolor1; }
+ .watercolor2 { --wc : @watercolor2; }
+ .watercolor3 { --wc : @watercolor3; }
+ .watercolor4 { --wc : @watercolor4; }
+ .watercolor5 { --wc : @watercolor5; }
+ .watercolor6 { --wc : @watercolor6; }
+ .watercolor7 { --wc : @watercolor7; }
+ .watercolor8 { --wc : @watercolor8; }
+ .watercolor9 { --wc : @watercolor9; }
.watercolor10 { --wc : @watercolor10; }
.watercolor11 { --wc : @watercolor11; }
.watercolor12 { --wc : @watercolor12; }
@@ -389,19 +375,19 @@ body {
.monster {
.useSansSerif();
&.frame {
- border-style : solid;
- border-width : 7px 6px;
+ width : calc(100% + 0.32cm);
+ padding : 4px 2px;
+ margin-right : -0.16cm;
+ margin-left : -0.16cm;
background-color : var(--HB_Color_MonsterStatBackground);
background-image : @monsterBlockBackground;
+ background-attachment : fixed;
+ background-blend-mode : overlay;
+ border-style : solid;
+ border-width : 7px 6px;
border-image : @monsterBorderImage 14 round;
border-image-outset : 0px 2px;
- background-blend-mode : overlay;
- background-attachment : fixed;
- box-shadow : 1px 4px 14px #888;
- padding : 4px 2px;
- margin-left : -0.16cm;
- margin-right : -0.16cm;
- width : calc(100% + 0.32cm);
+ box-shadow : 1px 4px 14px #888888;
}
position : relative;
@@ -409,38 +395,36 @@ body {
margin-bottom : 0.325cm;
//Headers
- h2{
+ h2 {
+ margin : 0;
font-size : 0.62cm;
line-height : 1em;
- margin : 0;
- &+p {
- font-size : 0.304cm; //Monster size and type subtext
+ & + p {
margin-bottom : 0;
+ font-size : 0.304cm; //Monster size and type subtext
}
}
- h3{
- font-family : ScalySansRemake;
+ h3 {
+ // margin-top : 0.05cm; //Font is misaligned. Shift up slightly
+ padding-bottom : 0.05cm;
+ font-family : "ScalySansRemake";
font-weight : 800;
font-variant : small-caps;
border-bottom : 2px solid var(--HB_Color_HeaderText);
- // margin-top : 0.05cm; //Font is misaligned. Shift up slightly
- padding-bottom : 0.05cm;
}
//Triangle dividers
- hr{
- visibility : visible;
+ hr {
height : 6px;
margin : 0.12cm 0cm;
+ visibility : visible;
background-image : @redTriangleImage;
background-size : 100% 100%;
border : none;
}
//Attribute Lists - All text between HRs is red
- hr ~ :is(dl,p) {
- color : var(--HB_Color_HeaderText);
- }
+ hr ~ :is(dl,p) { color : var(--HB_Color_HeaderText); }
hr:last-of-type {
& ~ :is(dl,p) {
color : inherit; // After the HRs, reset text to black
@@ -451,7 +435,7 @@ body {
}
// Monster Ability table
- hr + table:first-of-type{
+ hr + table:first-of-type {
margin : 0;
column-span : none;
color : var(--HB_Color_HeaderText);
@@ -459,51 +443,41 @@ body {
border-style : none;
border-image : none;
-webkit-column-span : none;
- tr {
- background-color : transparent;
- }
- td,th {
- padding: 0px;
- }
+ tr { background-color : transparent; }
+ td,th { padding : 0px; }
}
- :last-child {
- margin-bottom : 0;
- }
+ :last-child { margin-bottom : 0; }
}
//Full Width
- .monster.wide{
+ .monster.wide {
.useColumns(0.96, @fillMode: balance);
}
//*****************************
// * FOOTER
// *****************************/
- &:after{
- content : "";
+ &:after {
position : absolute;
bottom : 0px;
left : 0px;
z-index : 100;
- height : 50px;
width : 100%;
+ height : 50px;
+ content : '';
background-image : @footerAccentImage;
background-size : cover;
}
- &:nth-child(even){
- &:after{
- transform : scaleX(-1);
- }
- .pageNumber{
- left : 2px;
- }
- .footnote{
+ &:nth-child(even) {
+ &::after { transform : scaleX(-1); }
+ .pageNumber { left : 2px; }
+ .footnote {
left : 80px;
text-align : left;
}
}
- .pageNumber{
+ .pageNumber {
position : absolute;
right : 2px;
bottom : 22px;
@@ -512,11 +486,9 @@ body {
color : var(--HB_Color_Footnotes);
text-align : center;
text-indent : 0;
- &.auto::after {
- content : counter(phb-page-numbers);
- }
+ &.auto::after { content : counter(phb-page-numbers); }
}
- .footnote{
+ .footnote {
position : absolute;
right : 80px;
bottom : 32px;
@@ -529,41 +501,39 @@ body {
//************************************
// * CODE BLOCKS
// ************************************/
- code{
- font-family : "Courier New", Courier, monospace;
- font-size : 0.325;
+ code {
padding : 0px 4px;
- color : #58180d;
- background-color : #faf7ea;
- border-radius : 4px;
- white-space : pre-wrap;
+ font-family : 'Courier New', "Courier", monospace;
+ font-size : 0.325;
+ color : #58180D;
overflow-wrap : break-word;
+ white-space : pre-wrap;
+ background-color : #FAF7EA;
+ border-radius : 4px;
}
- pre code{
- width : 100%;
+ pre code {
display : inline-block;
+ width : 100%;
+ padding : 0.15cm;
+ margin-bottom : 2px;
border-style : solid;
border-width : 1px;
+ border-radius : 12px;
border-image : @codeBorderImage 26 stretch;
border-image-width : 10px;
border-image-outset : 2px;
- border-radius : 12px;
- margin-bottom : 2px;
- padding : 0.15cm;
.page :where(&) {
margin-top : 2px; //Prevent top border getting cut off on colbreak
}
- & + * {
- margin-top : 0.325cm;
- }
+ & + * { margin-top : 0.325cm; }
}
//*****************************
// * EXTRAS
// *****************************/
- hr{
- visibility : hidden;
+ hr {
margin : 0px;
+ visibility : hidden;
}
.columnSplit {
visibility : hidden;
@@ -572,22 +542,20 @@ body {
-moz-column-break-after : always;
}
//Avoid breaking up
- blockquote,table{
+ blockquote,table {
z-index : 15;
-webkit-column-break-inside : avoid;
page-break-inside : avoid;
break-inside : avoid;
}
//Text indent right after table
- table+p{
- text-indent : 1em;
- }
+ table + p { text-indent : 1em; }
// Nested lists
- ul ul,ol ol,ul ol,ol ul{
+ ul ul,ol ol,ul ol,ol ul {
margin-bottom : 0px;
margin-left : 1.5em;
}
- li{
+ li {
-webkit-column-break-inside : avoid;
page-break-inside : avoid;
break-inside : avoid;
@@ -596,86 +564,72 @@ body {
//*****************************
// * SPELL LIST
// *****************************/
-.page .spellList{
+.page .spellList {
.useSansSerif();
column-count : 2;
- ul+h5{
- margin-top : 15px;
- }
- p, ul{
+ ul + h5 { margin-top : 15px; }
+ p, ul {
font-size : 0.352cm;
line-height : 1.265em;
}
- ul{
- margin-bottom : 0.5em;
+ ul {
padding-left : 1em;
+ margin-bottom : 0.5em;
text-indent : -1em;
list-style-type : none;
-webkit-column-break-inside : auto;
page-break-inside : auto;
break-inside : auto;
}
- &.wide{
- column-count : 4;
- }
+ &.wide { column-count : 4; }
}
//*****************************
// * CLASS TABLE
// *****************************/
-.page .classTable{
-th[colspan]:not([rowspan]) {
- white-space : nowrap;
-}
-&.frame {
- margin-top : 0.7cm;
- margin-bottom : 0.9cm;
- margin-left : -0.1cm;
- margin-right : -0.1cm;
- width : calc(100% + 0.2cm);
- border-collapse : separate;
- background-color : white;
- border : initial;
- border-style : solid;
- border-image-outset : 0.4cm 0.3cm;
- border-image-repeat : stretch;
- border-image-slice : 200;
- border-image-source : @frameBorderImage;
- border-image-width : 47px;
- &.wide:first-child {
- margin-top: 0.12cm;
+.page .classTable {
+ th[colspan]:not([rowspan]) { white-space : nowrap; }
+ &.frame {
+ width : calc(100% + 0.2cm);
+ margin-top : 0.7cm;
+ margin-right : -0.1cm;
+ margin-bottom : 0.9cm;
+ margin-left : -0.1cm;
+ border-collapse : separate;
+ background-color : white;
+ border : initial;
+ border-style : solid;
+ border-image-source : @frameBorderImage;
+ border-image-slice : 200;
+ border-image-width : 47px;
+ border-image-outset : 0.4cm 0.3cm;
+ border-image-repeat : stretch;
+ &.wide:first-child { margin-top : 0.12cm; }
+ & + * { margin-top : 0; }
}
- & + * {
- margin-top: 0;
+ &.decoration { position : relative; }
+ &.decoration::before {
+ position : absolute;
+ top : 50%;
+ left : 50%;
+ z-index : -1;
+ width : 7.75cm;
+ height : calc(100% + 3.3cm);
+ content : '';
+ background-image : @classTableDecoration,
+ @classTableDecoration;
+ filter : drop-shadow(0px 0px 1px #C8C5C080);
+ background-repeat : no-repeat, no-repeat;
+ background-position : top, bottom;
+ background-size : contain, contain;
+ transform : translateY(-50%) translateX(-50%);
}
-}
-&.decoration {
- position:relative;
-}
-&.decoration::before {
- content :'';
- position : absolute;
- background-image : @classTableDecoration,
- @classTableDecoration;
- background-size : contain, contain;
- background-repeat : no-repeat, no-repeat;
- background-position : top, bottom;
- width : 7.75cm;
- height : calc(100% + 3.3cm);
- top : 50%;
- left : 50%;
- transform : translateY(-50%) translateX(-50%);
- filter : drop-shadow(0px 0px 1px #C8C5C080);
- z-index : -1;
-}
-&.decoration.wide::before {
- width : calc(100% + 3.3cm);
- height : 7.75cm;
- background-position : left, right;
-}
-h5 + table{
- margin-top : 0.2cm;
-}
+ &.decoration.wide::before {
+ width : calc(100% + 3.3cm);
+ height : 7.75cm;
+ background-position : left, right;
+ }
+ h5 + table { margin-top : 0.2cm; }
}
//*****************************
// * FRONT COVER PAGE
@@ -683,93 +637,91 @@ h5 + table{
.page:has(.frontCover) {
columns : 1;
text-align : center;
- &:after {
- all: unset;
- }
+ &::after { all : unset; }
h1 {
- text-shadow: unset;
- filter : drop-shadow(0 0 1.5px black) drop-shadow(0 0 0 black)
- drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
- drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
- drop-shadow(0 0 0 black) drop-shadow(0 0 0 black);
- text-transform : uppercase;
- font-weight : normal;
margin-top : 1.2cm;
margin-bottom : 0;
- color : white;
- font-family : NodestoCapsCondensed;
+ font-family : "NodestoCapsCondensed";
font-size : 2.245cm;
+ font-weight : normal;
line-height : 0.85em;
+ color : white;
+ text-shadow : unset;
+ text-transform : uppercase;
+ filter : drop-shadow(0 0 1.5px black) drop-shadow(0 0 0 black)
+ drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
+ drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
+ drop-shadow(0 0 0 black) drop-shadow(0 0 0 black);
}
h2 {
- filter : drop-shadow(0 0 1px black) drop-shadow(0 0 0 black)
- drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
- drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
- drop-shadow(0 0 0 black) drop-shadow(0 0 0 black);
- font-family : NodestoCapsCondensed;
- font-weight : normal;
+ font-family : "NodestoCapsCondensed";
font-size : 0.85cm;
- letter-spacing : 0.1cm;
+ font-weight : normal;
color : white;
+ letter-spacing : 0.1cm;
+ filter : drop-shadow(0 0 1px black) drop-shadow(0 0 0 black)
+ drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
+ drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
+ drop-shadow(0 0 0 black) drop-shadow(0 0 0 black);
}
hr {
- display : block;
position : relative;
- background-image : @horizontalRule;
- background-size : 100% 100%;
- visibility : visible;
- height : 0.5cm;
+ display : block;
width : 12cm;
- border : none;
+ height : 0.5cm;
margin : auto;
+ visibility : visible;
+ background-image : @horizontalRule;
filter : drop-shadow(0 0 3px black);
+ background-size : 100% 100%;
+ border : none;
}
.banner {
- filter : drop-shadow(2px 2px 2px black);
position : absolute;
- left : 0;
bottom : 4.2cm;
- background-image : url('/assets/coverPageBanner.svg');
- height : 1.7cm;
- width : 10.5cm;
- color : white;
- font-family : NodestoCapsCondensed;
- font-weight : normal;
- font-size : 1cm;
- letter-spacing : 0.014cm;
- text-align : left;
- padding-left : 1cm;
+ left : 0;
display : flex;
- justify-content : center;
flex-direction : column;
+ justify-content : center;
+ width : 10.5cm;
+ height : 1.7cm;
padding-top : 0.1cm;
+ padding-left : 1cm;
+ font-family : "NodestoCapsCondensed";
+ font-size : 1cm;
+ font-weight : normal;
+ color : white;
+ text-align : left;
+ letter-spacing : 0.014cm;
+ background-image : url('/assets/coverPageBanner.svg');
+ filter : drop-shadow(2px 2px 2px black);
}
.footnote {
- filter : drop-shadow(0 0 0.7px black) drop-shadow(0 0 0 black)
- drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
- drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
- drop-shadow(0 0 0 black) drop-shadow(0 0 0 black);
position : absolute;
- text-align : center;
- color : white;
- font-size : 0.496cm;
+ right : 0;
bottom : 1.3cm;
left : 0;
- right : 0;
- margin-left : auto;
- margin-right : auto;
width : 70%;
- font-family : Overpass;
+ margin-right : auto;
+ margin-left : auto;
+ font-family : "Overpass";
+ font-size : 0.496cm;
+ color : white;
+ text-align : center;
+ filter : drop-shadow(0 0 0.7px black) drop-shadow(0 0 0 black)
+ drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
+ drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
+ drop-shadow(0 0 0 black) drop-shadow(0 0 0 black);
}
.logo {
position : absolute;
top : 0.5cm;
- left : 0;
right : 0;
+ left : 0;
filter : drop-shadow(0 0 0.075cm black);
img {
- height : 2cm;
width : 100%;
+ height : 2cm;
}
}
}
@@ -779,162 +731,233 @@ h5 + table{
.page:has(.insideCover) {
columns : 1;
text-align : center;
- &:after {
- all : unset;
- }
+ &::after { all : unset; }
h1 {
- font-family : NodestoCapsCondensed;
- font-weight : normal;
- font-size : 2.1cm;
margin-top : 1.2cm;
margin-bottom : 0;
- text-transform : uppercase;
+ font-family : "NodestoCapsCondensed";
+ font-size : 2.1cm;
+ font-weight : normal;
line-height : 0.85em;
+ text-transform : uppercase;
}
h2 {
- font-family : NodestoCapsCondensed;
- font-weight : normal;
+ font-family : "NodestoCapsCondensed";
font-size : 0.85cm;
+ font-weight : normal;
letter-spacing : 0.5cm;
}
hr {
- display : block;
position : relative;
+ display : block;
+ width : 12cm;
+ height : 0.5cm;
+ margin : auto;
+ visibility : visible;
background-image : @horizontalRule;
background-size : 100% 100%;
- visibility : visible;
- height : 0.5cm;
- width : 12cm;
border : none;
- margin : auto;
}
.logo {
position : absolute;
+ right : 0;
bottom : 1cm;
- left : 0;
- right : 0;
- height : 2cm;
+ left : 0;
+ height : 2cm;
img {
- height : 2cm;
width : 100%;
+ height : 2cm;
+ }
+ }
+}
+//*****************************
+// * BACK COVER
+// *****************************/
+.page:has(.backCover) {
+ padding : 2.25cm 1.3cm 2cm 1.3cm;
+ color : #FFFFFF;
+ columns : 1;
+ &::after { all : unset; }
+ .columnWrapper { width : 7.6cm; }
+ .backCover {
+ position : absolute;
+ inset : 0;
+ z-index : -1;
+ width : 11cm;
+ background-image : @backCover;
+ background-repeat : no-repeat;
+ background-size : contain;
+ }
+ .blank { height : 1.4em; }
+ h1 {
+ margin-bottom : 0.3cm;
+ font-family : "NodestoCapsCondensed";
+ font-size : 1.35cm;
+ line-height : 0.95em;
+ color : #ED1C24;
+ text-align : center;
+ }
+ h1 + p::first-line,
+ h1 + p::first-letter { all : unset; }
+ img {
+ position : absolute;
+ top : 0px;
+ z-index : -2;
+ height : 100%;
+ }
+ hr {
+ width : 4.5cm;
+ height : 0.53cm;
+ margin-top : 1.1cm;
+ margin-right : auto;
+ margin-left : auto;
+ visibility : visible;
+ background-image : @horizontalRule;
+ background-size : 100% 100%;
+ border : none;
+ }
+ p {
+ font-family : "Overpass";
+ font-size : 0.332cm;
+ line-height : 1.5em;
+ }
+ hr + p {
+ margin-top : 0.6cm;
+ text-align : center;
+ }
+ .logo {
+ position : absolute;
+ bottom : 2cm;
+ left : 1.2cm;
+ z-index : 0;
+ width : 7.6cm;
+ height : 1.5cm;
+ img {
+ position : relative;
+ z-index : 0;
+ width : 100%;
+ height : 1.5cm;
+ }
+ p {
+ position : relative;
+ width : 100%;
+ font-family : "NodestoCapsWide";
+ font-size : 0.4cm;
+ line-height : 1em;
+ color : #FFFFFF;
+ text-align : center;
+ text-indent : 0;
+ letter-spacing : 0.08em;
}
}
}
//*****************************
- // * PART COVER
+// * PART COVER
// *****************************/
.page:has(.partCover) {
- columns : 1;
- text-align : center;
- padding-top: 0;
+ padding-top : 0;
+ text-align : center;
+ columns : 1;
- .partCover {
- background-image: @partCoverHeaderPHB;
- background-repeat: no-repeat;
- position: absolute;
- background-size: 100%;
- top: 0;
- left: 0;
- height: 6cm;
- width: 100%;
- }
-
- h1 {
- position: relative;
- text-align: center;
- text-transform: uppercase;
- font-size: 2.3cm;
- font-family: NodestoCapsCondensed;
- margin-top: .4cm;
- }
-
- h2 {
- font-family: Overpass;
- font-size: 0.45cm;
- position: relative;
- margin-top: -0.7em;
- line-height: 1.1em;
- margin-left : auto;
- margin-right : auto;
- }
+ .partCover {
+ position : absolute;
+ top : 0;
+ left : 0;
+ width : 100%;
+ height : 6cm;
+ background-image : @partCoverHeaderPHB;
+ background-repeat : no-repeat;
+ background-size : 100%;
}
+ h1 {
+ position : relative;
+ margin-top : 0.4cm;
+ font-family : "NodestoCapsCondensed";
+ font-size : 2.3cm;
+ text-align : center;
+ text-transform : uppercase;
+ }
+
+ h2 {
+ position : relative;
+ margin-top : -0.7em;
+ margin-right : auto;
+ margin-left : auto;
+ font-family : "Overpass";
+ font-size : 0.45cm;
+ line-height : 1.1em;
+ }
+}
+
//*****************************
// * TABLE OF CONTENTS
// *****************************/
.page {
-&:has(.toc):after {
- display: none;
-}
-.toc {
--webkit-column-break-inside : avoid;
-page-break-inside : avoid;
-break-inside : avoid;
- h1 {
- text-align : center;
- margin-bottom : 0.3cm;
- }
- a{
- display : inline;
- color : inherit;
- text-decoration : none;
- &:hover{
- text-decoration : underline;
+ &:has(.toc)::after { display : none; }
+ .toc {
+ -webkit-column-break-inside : avoid;
+ page-break-inside : avoid;
+ break-inside : avoid;
+ h1 {
+ margin-bottom : 0.3cm;
+ text-align : center;
}
- }
- h4 {
- margin-top : 0.2cm;
- line-height : 0.4cm;
- & + ul li {
- line-height: 1.2em;
- }
- }
- ul{
- padding-left : 0;
- list-style-type : none;
- margin-top : 0;
a {
- width : 100%;
- display : flex;
- flex-flow : row nowrap;
- justify-content : space-between;
+ display : inline;
+ color : inherit;
+ text-decoration : none;
+ &:hover { text-decoration : underline; }
}
- li + li h3 {
- margin-top : 0.26cm;
- line-height : 1em
+ h4 {
+ margin-top : 0.2cm;
+ line-height : 0.4cm;
+ & + ul li { line-height : 1.2em; }
}
- h3 span:first-child::after {
- border : none;
- }
- span {
- display : contents;
- &:first-child::after {
- content : "";
- bottom : 0.08cm;
- flex : 1;
- margin-left : 0.08cm; /* Spacing before dot leaders */
- margin-right : 0.16cm;
- border-bottom : 0.05cm dotted #000;
- margin-bottom : 0.08cm;
+ ul {
+ padding-left : 0;
+ margin-top : 0;
+ list-style-type : none;
+ a {
+ display : flex;
+ flex-flow : row nowrap;
+ justify-content : space-between;
+ width : 100%;
}
- &:last-child {
- display : inline-block;
- align-self : flex-end;
- font-family : "BookInsanityRemake";
- font-size : 0.34cm;
- font-weight : normal;
- color : #000;
+ li + li h3 {
+ margin-top : 0.26cm;
+ line-height : 1em;
+ }
+ h3 span:first-child::after { border : none; }
+ span {
+ display : contents;
+ &:first-child::after {
+ bottom : 0.08cm;
+ flex : 1;
+ margin-right : 0.16cm;
+ margin-bottom : 0.08cm;
+ margin-left : 0.08cm; /* Spacing before dot leaders */
+ content : '';
+ border-bottom : 0.05cm dotted #000000;
+ }
+ &:last-child {
+ display : inline-block;
+ align-self : flex-end;
+ font-family : 'BookInsanityRemake';
+ font-size : 0.34cm;
+ font-weight : normal;
+ color : #000000;
+ }
+ }
+ ul { /* List indent */
+ margin-left : 1em;
}
}
- ul { /*List indent*/
- margin-left : 1em;
+ &.wide {
+ .useColumns(0.96, @fillMode: balance);
}
}
- &.wide{
- .useColumns(0.96, @fillMode: balance);
- }
-}
}
//*****************************
@@ -942,78 +965,85 @@ break-inside : avoid;
// *****************************/
.page {
dl {
- line-height : 1.25em;
padding-left : 1em;
+ line-height : 1.25em;
white-space : pre-line;
- & + * {
- margin-top : 0.28cm;
- }
+ & + * { margin-top : 0.28cm; }
}
- dl + * {
- margin-top : 0.17cm;
- }
- p + dl {
- margin-top: 0.17cm;
- }
- dt {
- display : inline;
- margin-right : 5px;
+ dl + * { margin-top : 0.17cm; }
+ p + dl { margin-top : 0.17cm; }
+ dt {
+ display : inline;
+ margin-right : 5px;
margin-left : -1em;
- }
- dd {
+ }
+ dd {
display : inline;
margin-left : 0px;
text-indent : 0px;
- }
+ }
}
//*****************************
// * WIDE
// *****************************/
-.page .wide{
- margin-bottom : 0.325cm;
-}
+.page .wide { margin-bottom : 0.325cm; }
-.page h1 + *{
- margin-top : 0;
-}
+.page h1 + * { margin-top : 0; }
//*****************************
// * RUNE TABLE
// *****************************/
.page {
.runeTable {
- margin-block: 0.7cm;
+ margin-block : 0.7cm;
table {
font-family : inherit;
- tbody tr {
- background: unset;
- }
+ tbody tr { background : unset; }
th, td {
- width: 1.3cm;
- height: 1.3cm;
- vertical-align: middle;
- text-transform: uppercase;
- outline: 1px solid #000;
- font-weight: normal;
+ width : 1.3cm;
+ height : 1.3cm;
+ font-weight : normal;
+ text-transform : uppercase;
+ vertical-align : middle;
+ outline : 1px solid #000000;
}
- th{
- font-family: BookInsanityRemake;
- font-size: 0.45cm;
- }
- td {
- font-size: 0.7cm;
+ th {
+ font-family : "BookInsanityRemake";
+ font-size : 0.45cm;
}
+ td { font-size : 0.7cm; }
}
&.frame {
- border: initial;
- border-style: solid;
- border-image-outset: .45cm .35cm .4cm .4cm;
- border-image-repeat: stretch;
- border-image-slice: 170;
- border-image-source: @scriptBorder;
- border-image-width: 1.4cm;
+ border : initial;
+ border-style : solid;
+ border-image-source : @scriptBorder;
+ border-image-slice : 170;
+ border-image-width : 1.4cm;
+ border-image-outset : 0.45cm 0.35cm 0.4cm 0.4cm;
+ border-image-repeat : stretch;
+ }
+ }
+}
+//*****************************
+// * INDEX
+// *****************************/
+.page {
+ .index {
+ font-size : 0.218cm;
+
+ ul ul { margin : 0; }
+
+ ul {
+ padding-left : 0;
+ text-indent : 0;
+ list-style-type : none;
+ }
+
+ & > ul > li {
+ padding-left : 1.5em;
+ text-indent : -1.5em;
}
}
}
diff --git a/themes/V3/Blank/snippets.js b/themes/V3/Blank/snippets.js
index 9d64496c3..72372c297 100644
--- a/themes/V3/Blank/snippets.js
+++ b/themes/V3/Blank/snippets.js
@@ -2,6 +2,7 @@
const WatercolorGen = require('./snippets/watercolor.gen.js');
const ImageMaskGen = require('./snippets/imageMask.gen.js');
+const FooterGen = require('./snippets/footer.gen.js');
const dedent = require('dedent-tabs').default;
module.exports = [
@@ -21,6 +22,53 @@ module.exports = [
icon : 'fas fa-file-alt',
gen : '\n\\page\n'
},
+ {
+ name : 'Page Number',
+ icon : 'fas fa-bookmark',
+ gen : '{{pageNumber 1}}\n'
+ },
+ {
+ name : 'Auto-incrementing Page Number',
+ icon : 'fas fa-sort-numeric-down',
+ gen : '{{pageNumber,auto}}\n'
+ },
+ {
+ name : 'Footer',
+ icon : 'fas fa-shoe-prints',
+ gen : FooterGen.createFooterFunc(),
+ subsnippets : [
+ {
+ name : 'Footer from H1',
+ icon : 'fas fa-dice-one',
+ gen : FooterGen.createFooterFunc(1)
+ },
+ {
+ name : 'Footer from H2',
+ icon : 'fas fa-dice-two',
+ gen : FooterGen.createFooterFunc(2)
+ },
+ {
+ name : 'Footer from H3',
+ icon : 'fas fa-dice-three',
+ gen : FooterGen.createFooterFunc(3)
+ },
+ {
+ name : 'Footer from H4',
+ icon : 'fas fa-dice-four',
+ gen : FooterGen.createFooterFunc(4)
+ },
+ {
+ name : 'Footer from H5',
+ icon : 'fas fa-dice-five',
+ gen : FooterGen.createFooterFunc(5)
+ },
+ {
+ name : 'Footer from H6',
+ icon : 'fas fa-dice-six',
+ gen : FooterGen.createFooterFunc(6)
+ }
+ ]
+ },
{
name : 'Vertical Spacing',
icon : 'fas fa-arrows-alt-v',
diff --git a/themes/V3/Blank/snippets/footer.gen.js b/themes/V3/Blank/snippets/footer.gen.js
new file mode 100644
index 000000000..6583cd06e
--- /dev/null
+++ b/themes/V3/Blank/snippets/footer.gen.js
@@ -0,0 +1,17 @@
+const Markdown = require('../../../../shared/naturalcrit/markdown.js');
+
+module.exports = {
+ createFooterFunc : function(headerSize=1){
+ return (props)=>{
+ const cursorPos = props.cursorPos;
+
+ const markdownText = props.brew.text.split('\n').slice(0, cursorPos.line).join('\n');
+ const markdownTokens = Markdown.marked.lexer(markdownText);
+ const headerToken = markdownTokens.findLast((lexerToken)=>{ return lexerToken.type === 'heading' && lexerToken.depth === headerSize; });
+ const headerText = headerToken?.tokens.map((token)=>{ return token.text; }).join('');
+ const outputText = headerText || 'PART 1 | SECTION NAME';
+
+ return `\n{{footnote ${outputText}}}\n`;
+ };
+ }
+};
\ No newline at end of file
diff --git a/themes/V3/Blank/style.less b/themes/V3/Blank/style.less
index 38aa42f20..f233a2347 100644
--- a/themes/V3/Blank/style.less
+++ b/themes/V3/Blank/style.less
@@ -33,7 +33,7 @@ body {
}
}
-.useColumns(@multiplier : 1, @fillMode: balance){
+.useColumns(@multiplier : 1, @fillMode: auto){
column-fill : @fillMode;
column-count : 2;
}
@@ -42,6 +42,7 @@ body {
column-span : all;
columns : inherit;
column-gap : inherit;
+ column-fill : inherit;
}
.page{
.useColumns();
diff --git a/themes/assets/assets.less b/themes/assets/assets.less
index 0547e8c70..cdef32c7c 100644
--- a/themes/assets/assets.less
+++ b/themes/assets/assets.less
@@ -16,6 +16,7 @@
@partCoverHeaderPHB : url('/assets/partCoverHeaderPHB.png');
@partCoverHeaderDMG : url('/assets/partCoverHeaderDMG.svg');
@insideCoverMask : url('/assets/insideCoverMask.png');
+@backCover : url('/assets/backCover.png');
@scriptBorder : url('/assets/scriptBorder.png');
// Watercolor Images
diff --git a/themes/assets/backCover.png b/themes/assets/backCover.png
new file mode 100644
index 000000000..5c6a14d25
Binary files /dev/null and b/themes/assets/backCover.png differ
diff --git a/themes/assets/naturalCritLogo.svg b/themes/assets/naturalCritLogoRed.svg
similarity index 100%
rename from themes/assets/naturalCritLogo.svg
rename to themes/assets/naturalCritLogoRed.svg
diff --git a/themes/assets/naturalCritLogoWhite.svg b/themes/assets/naturalCritLogoWhite.svg
new file mode 100644
index 000000000..56b820776
--- /dev/null
+++ b/themes/assets/naturalCritLogoWhite.svg
@@ -0,0 +1,50 @@
+
+
diff --git a/themes/codeMirror/customEditorStyles.less b/themes/codeMirror/customEditorStyles.less
new file mode 100644
index 000000000..3318e1305
--- /dev/null
+++ b/themes/codeMirror/customEditorStyles.less
@@ -0,0 +1,88 @@
+.editor .codeEditor .CodeMirror {
+ // Themes with dark backgrounds
+ &.cm-s-3024-night,
+ &.cm-s-abbott,
+ &.cm-s-abcdef,
+ &.cm-s-ambiance,
+ &.cm-s-ayu-dark,
+ &.cm-s-ayu-mirage,
+ &.cm-s-base16-dark,
+ &.cm-s-bespin,
+ &.cm-s-blackboard,
+ &.cm-s-cobalt,
+ &.cm-s-colorforth,
+ &.cm-s-darcula,
+ &.cm-s-dracula,
+ &.cm-s-duotone-dark,
+ &.cm-s-erlang-dark,
+ &.cm-s-gruvbox-dark,
+ &.cm-s-hopscotch,
+ &.cm-s-icecoder,
+ &.cm-s-isotope,
+ &.cm-s-lesser-dark,
+ &.cm-s-liquibyte,
+ &.cm-s-lucario,
+ &.cm-s-material,
+ &.cm-s-material-darker,
+ &.cm-s-material-ocean,
+ &.cm-s-material-palenight,
+ &.cm-s-mbo,
+ &.cm-s-midnight,
+ &.cm-s-monokai,
+ &.cm-s-moxer,
+ &.cm-s-night,
+ &.cm-s-nord,
+ &.cm-s-oceanic-next,
+ &.cm-s-panda-syntax,
+ &.cm-s-paraiso-dark,
+ &.cm-s-pastel-on-dark,
+ &.cm-s-railscasts,
+ &.cm-s-rubyblue,
+ &.cm-s-seti,
+ &.cm-s-shadowfox,
+ &.cm-s-the-matrix,
+ &.cm-s-tomorrow-night-bright,
+ &.cm-s-tomorrow-night-eighties,
+ &.cm-s-twilight,
+ &.cm-s-vibrant-ink,
+ &.cm-s-xq-dark,
+ &.cm-s-yonce,
+ &.cm-s-zenburn
+ {
+ .CodeMirror-code {
+ .block:not(.cm-comment) {
+ color: magenta;
+ }
+ .columnSplit {
+ color: black;
+ background-color: rgba(35,153,153,0.5);
+ }
+ .pageLine {
+ background-color: rgba(255,255,255,0.75);
+ & ~ pre.CodeMirror-line {
+ color: black;
+ }
+ }
+ }
+ }
+ // Themes with light backgrounds
+ &.cm-s-default,
+ &.cm-s-3024-day,
+ &.cm-s-ambiance-mobile,
+ &.cm-s-base16-light,
+ &.cm-s-duotone-light,
+ &.cm-s-eclipse,
+ &.cm-s-elegant,
+ &.cm-s-juejin,
+ &.cm-s-neat,
+ &.cm-s-neo,
+ &.cm-s-paraiso-lightm
+ &.cm-s-solarized,
+ &.cm-s-ssms,
+ &.cm-s-ttcn,
+ &.cm-s-xq-light,
+ &.cm-s-yeti {
+ // Future styling for themes with light backgrounds
+ --dummyVar: 'currently unused';
+ }
+}
\ No newline at end of file
diff --git a/themes/codeMirror/editorThemes.json b/themes/codeMirror/editorThemes.json
new file mode 100644
index 000000000..a4dd74470
--- /dev/null
+++ b/themes/codeMirror/editorThemes.json
@@ -0,0 +1,68 @@
+[
+"default",
+"3024-day",
+"3024-night",
+"abbott",
+"abcdef",
+"ambiance-mobile",
+"ambiance",
+"ayu-dark",
+"ayu-mirage",
+"base16-dark",
+"base16-light",
+"bespin",
+"blackboard",
+"cobalt",
+"colorforth",
+"darcula",
+"dracula",
+"duotone-dark",
+"duotone-light",
+"eclipse",
+"elegant",
+"erlang-dark",
+"gruvbox-dark",
+"hopscotch",
+"icecoder",
+"idea",
+"isotope",
+"juejin",
+"lesser-dark",
+"liquibyte",
+"lucario",
+"material-darker",
+"material-ocean",
+"material-palenight",
+"material",
+"mbo",
+"mdn-like",
+"midnight",
+"monokai",
+"moxer",
+"neat",
+"neo",
+"night",
+"nord",
+"oceanic-next",
+"panda-syntax",
+"paraiso-dark",
+"paraiso-light",
+"pastel-on-dark",
+"railscasts",
+"rubyblue",
+"seti",
+"shadowfox",
+"solarized",
+"ssms",
+"the-matrix",
+"tomorrow-night-bright",
+"tomorrow-night-eighties",
+"ttcn",
+"twilight",
+"vibrant-ink",
+"xq-dark",
+"xq-light",
+"yeti",
+"yonce",
+"zenburn"
+]
diff --git a/themes/fonts/5e/Nodesto Caps Wide.woff2 b/themes/fonts/5e/Nodesto Caps Wide.woff2
new file mode 100644
index 000000000..d50a19915
Binary files /dev/null and b/themes/fonts/5e/Nodesto Caps Wide.woff2 differ
diff --git a/themes/fonts/5e/fonts.less b/themes/fonts/5e/fonts.less
index a83399567..8f089b51c 100644
--- a/themes/fonts/5e/fonts.less
+++ b/themes/fonts/5e/fonts.less
@@ -107,6 +107,13 @@
font-style: italic;
}
+@font-face {
+ font-family: NodestoCapsWide;
+ src: url('../../../fonts/5e/Nodesto Caps Wide.woff2');
+ font-weight: normal;
+ font-style: normal
+}
+
@font-face {
font-family: Overpass;
src: url('../../../fonts/5e/Overpass Medium.woff2');