Adds import support from our other project.
This commit is contained in:
155
rulesets/open5e/templates/character_card.css
Normal file
155
rulesets/open5e/templates/character_card.css
Normal file
@@ -0,0 +1,155 @@
|
||||
.card-page-wrapper {
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
background-color: #f4f6f8;
|
||||
font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.npc-card {
|
||||
width: 100%;
|
||||
max-width: 580px;
|
||||
background: #ffffff;
|
||||
border: 1.5px solid #922610;
|
||||
border-radius: 4px;
|
||||
padding: 16px 18px 18px;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.card-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.card-name {
|
||||
margin: 0;
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
color: #922610;
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
.card-cr {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
color: #555;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.card-subtitle {
|
||||
font-style: italic;
|
||||
font-size: 13.5px;
|
||||
color: #666;
|
||||
margin: 4px 0 14px 0;
|
||||
}
|
||||
|
||||
.stat-boxes-row {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.stat-box {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
border: 1.5px solid #222;
|
||||
border-radius: 2px;
|
||||
padding: 6px 2px 4px;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 48px;
|
||||
background: #ffffff;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.badge-advantage {
|
||||
position: absolute;
|
||||
top: 1.5px;
|
||||
left: 2.5px;
|
||||
font-size: 8.5px;
|
||||
font-weight: 800;
|
||||
color: #111;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.badge-proficiency {
|
||||
position: absolute;
|
||||
top: 1.5px;
|
||||
right: 2.5px;
|
||||
font-size: 8.5px;
|
||||
font-weight: 800;
|
||||
color: #111;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.stat-mod {
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
color: #111;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.stat-code {
|
||||
font-size: 9.5px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-top: 3px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.action-boxes-grid {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.action-card-box {
|
||||
flex: 1 1 calc(50% - 8px);
|
||||
min-width: 140px;
|
||||
border: 1px solid #333;
|
||||
border-radius: 2px;
|
||||
padding: 8px 10px;
|
||||
background: #ffffff;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.action-box-title {
|
||||
font-size: 13.5px;
|
||||
font-weight: 700;
|
||||
color: #222;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.action-box-desc {
|
||||
font-size: 11.5px;
|
||||
line-height: 1.35;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
/* Single action takes full width */
|
||||
.action-boxes-grid > .action-card-box:only-child {
|
||||
flex: 1 1 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 440px) {
|
||||
.card-page-wrapper {
|
||||
padding: 8px;
|
||||
}
|
||||
.stat-boxes-row {
|
||||
gap: 3px;
|
||||
}
|
||||
.stat-mod {
|
||||
font-size: 13px;
|
||||
}
|
||||
.action-card-box {
|
||||
flex: 1 1 100%;
|
||||
}
|
||||
}
|
||||
|
||||
118
rulesets/open5e/templates/character_card.html
Normal file
118
rulesets/open5e/templates/character_card.html
Normal file
@@ -0,0 +1,118 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{display_name}} - Character Card</title>
|
||||
<link rel="stylesheet" href="character_card.css">
|
||||
</head>
|
||||
<body class="card-page-wrapper">
|
||||
<div class="npc-card">
|
||||
<!-- Top Bar: Name & Challenge/Level -->
|
||||
<div class="card-top">
|
||||
<h2 class="card-name">{{display_name}}</h2>
|
||||
<div class="card-cr">
|
||||
{{#if properties.challenge_rating}}
|
||||
CR {{properties.challenge_rating}}
|
||||
{{else if properties.cr}}
|
||||
CR {{properties.cr}}
|
||||
{{else if properties.level}}
|
||||
LVL {{properties.level}}
|
||||
{{else}}
|
||||
CR 1
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Subtitle: Size, Type, Subtype, Alignment -->
|
||||
<div class="card-subtitle">
|
||||
{{#if properties.size}}{{properties.size}}{{else}}medium{{/if}}
|
||||
{{#if properties.type}}{{properties.type}}{{else}}Humanoid{{/if}}{{#if properties.subtype}} ({{properties.subtype}}){{else if properties.race}} ({{properties.race}}){{/if}},
|
||||
{{#if properties.alignment}}{{properties.alignment}}{{else}}unaligned{{/if}}
|
||||
</div>
|
||||
|
||||
<!-- 8 Stat Boxes Row: S, D, C, I, W, Ch, AC, HP -->
|
||||
<div class="stat-boxes-row">
|
||||
<!-- STR -->
|
||||
<div class="stat-box">
|
||||
<span class="badge-advantage">{{advBadge properties "str"}}</span>
|
||||
<span class="badge-proficiency">{{profBadge properties.saving_throws "str"}}</span>
|
||||
<span class="stat-mod">{{modText properties.abilities.strength}}</span>
|
||||
<span class="stat-code">S</span>
|
||||
</div>
|
||||
|
||||
<!-- DEX -->
|
||||
<div class="stat-box">
|
||||
<span class="badge-advantage">{{advBadge properties "dex"}}</span>
|
||||
<span class="badge-proficiency">{{profBadge properties.saving_throws "dex"}}</span>
|
||||
<span class="stat-mod">{{modText properties.abilities.dexterity}}</span>
|
||||
<span class="stat-code">D</span>
|
||||
</div>
|
||||
|
||||
<!-- CON -->
|
||||
<div class="stat-box">
|
||||
<span class="badge-advantage">{{advBadge properties "con"}}</span>
|
||||
<span class="badge-proficiency">{{profBadge properties.saving_throws "con"}}</span>
|
||||
<span class="stat-mod">{{modText properties.abilities.constitution}}</span>
|
||||
<span class="stat-code">C</span>
|
||||
</div>
|
||||
|
||||
<!-- INT -->
|
||||
<div class="stat-box">
|
||||
<span class="badge-advantage">{{advBadge properties "int"}}</span>
|
||||
<span class="badge-proficiency">{{profBadge properties.saving_throws "int"}}</span>
|
||||
<span class="stat-mod">{{modText properties.abilities.intelligence}}</span>
|
||||
<span class="stat-code">I</span>
|
||||
</div>
|
||||
|
||||
<!-- WIS -->
|
||||
<div class="stat-box">
|
||||
<span class="badge-advantage">{{advBadge properties "wis"}}</span>
|
||||
<span class="badge-proficiency">{{profBadge properties.saving_throws "wis"}}</span>
|
||||
<span class="stat-mod">{{modText properties.abilities.wisdom}}</span>
|
||||
<span class="stat-code">W</span>
|
||||
</div>
|
||||
|
||||
<!-- CHA -->
|
||||
<div class="stat-box">
|
||||
<span class="badge-advantage">{{advBadge properties "cha"}}</span>
|
||||
<span class="badge-proficiency">{{profBadge properties.saving_throws "cha"}}</span>
|
||||
<span class="stat-mod">{{modText properties.abilities.charisma}}</span>
|
||||
<span class="stat-code">Ch</span>
|
||||
</div>
|
||||
|
||||
<!-- AC -->
|
||||
<div class="stat-box">
|
||||
<span class="stat-mod">{{val properties.armor_class}}</span>
|
||||
<span class="stat-code">AC</span>
|
||||
</div>
|
||||
|
||||
<!-- HP -->
|
||||
<div class="stat-box">
|
||||
<span class="stat-mod">
|
||||
{{#if properties.hit_points.current}}
|
||||
{{properties.hit_points.current}}
|
||||
{{else if properties.hit_points.max}}
|
||||
{{properties.hit_points.max}}
|
||||
{{else}}
|
||||
{{val properties.hit_points}}
|
||||
{{/if}}
|
||||
</span>
|
||||
<span class="stat-code">HP</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action Summary Boxes -->
|
||||
{{#if properties.actions}}
|
||||
<div class="action-boxes-grid">
|
||||
{{#each properties.actions}}
|
||||
<div class="action-card-box">
|
||||
<div class="action-box-title">{{this.name}}</div>
|
||||
<div class="action-box-desc">{{formatText this.desc}}</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
245
rulesets/open5e/templates/character_list_item.css
Normal file
245
rulesets/open5e/templates/character_list_item.css
Normal file
@@ -0,0 +1,245 @@
|
||||
/* Character List Item Template - Uniform Fixed Height with Non-Wrapping Horizontal Actions */
|
||||
:root {
|
||||
--card-border: #922610;
|
||||
--card-title: #922610;
|
||||
--box-border: #222222;
|
||||
--bg-page: #f4f6f8;
|
||||
--bg-card: #ffffff;
|
||||
--bg-box: #ffffff;
|
||||
--text-main: #111111;
|
||||
--text-muted: #666666;
|
||||
--font-sans: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
body.list-item-page-wrapper {
|
||||
margin: 0;
|
||||
padding: 16px;
|
||||
background-color: var(--bg-page);
|
||||
font-family: var(--font-sans);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Card has fixed height so all list cards are identical height */
|
||||
.npc-list-card {
|
||||
width: 100%;
|
||||
max-width: 580px;
|
||||
height: 236px;
|
||||
background: var(--bg-card);
|
||||
border: 1.5px solid var(--card-border);
|
||||
border-radius: 4px;
|
||||
padding: 12px 14px;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
.card-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.card-name {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: var(--card-title);
|
||||
line-height: 1.15;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.card-cr {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #555;
|
||||
white-space: nowrap;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.card-subtitle {
|
||||
font-style: italic;
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
margin: 2px 0 10px 0;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 8 Stat Boxes */
|
||||
.stat-boxes-row {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.stat-box {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
border: 1.2px solid var(--box-border);
|
||||
border-radius: 2px;
|
||||
padding: 4px 1px 3px;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 44px;
|
||||
background: var(--bg-box);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.badge-advantage {
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
left: 2px;
|
||||
font-size: 8px;
|
||||
font-weight: 800;
|
||||
color: #111;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.badge-proficiency {
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
right: 2px;
|
||||
font-size: 8px;
|
||||
font-weight: 800;
|
||||
color: #111;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.stat-mod {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
color: var(--text-main);
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.stat-code {
|
||||
font-size: 9px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-top: 2px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
/* Actions Row: Single Non-Wrapping Row with Horizontal Scrolling */
|
||||
.action-row-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
min-height: 0; /* Allows container to shrink inside flex parent */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.action-boxes-row {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #ccc transparent;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.action-boxes-row::-webkit-scrollbar {
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
.action-boxes-row::-webkit-scrollbar-thumb {
|
||||
background: #ccc;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.action-boxes-row::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* Individual Action Card */
|
||||
.action-card-box {
|
||||
flex: 0 0 170px;
|
||||
max-width: 240px;
|
||||
border: 1px solid #333;
|
||||
border-radius: 2px;
|
||||
padding: 6px 8px;
|
||||
background: #ffffff;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* When only 1 action exists (e.g. Acolyte Club), expand full width */
|
||||
.action-boxes-row > .action-card-box:only-child {
|
||||
flex: 1 0 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.action-box-title {
|
||||
font-size: 12.5px;
|
||||
font-weight: 700;
|
||||
color: #222;
|
||||
margin-bottom: 2px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.action-box-desc {
|
||||
font-size: 10.5px;
|
||||
line-height: 1.25;
|
||||
color: #444;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* Empty State (e.g. Unnamed Monster) */
|
||||
.no-actions-box {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px dashed #ccc;
|
||||
border-radius: 2px;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.no-actions-text {
|
||||
font-size: 11px;
|
||||
color: #999;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 440px) {
|
||||
.list-item-page-wrapper {
|
||||
padding: 6px;
|
||||
}
|
||||
.stat-boxes-row {
|
||||
gap: 3px;
|
||||
}
|
||||
.stat-mod {
|
||||
font-size: 12px;
|
||||
}
|
||||
.action-card-box {
|
||||
flex: 0 0 145px;
|
||||
}
|
||||
}
|
||||
|
||||
127
rulesets/open5e/templates/character_list_item.html
Normal file
127
rulesets/open5e/templates/character_list_item.html
Normal file
@@ -0,0 +1,127 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<base href="/rulesets/open5e/templates/">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{display_name}} - List Item</title>
|
||||
<link rel="stylesheet" href="character_list_item.css">
|
||||
</head>
|
||||
<body class="list-item-page-wrapper">
|
||||
<div class="npc-list-card">
|
||||
|
||||
<!-- Header: Name & Challenge/Level -->
|
||||
<div class="card-top">
|
||||
<h2 class="card-name">{{display_name}}</h2>
|
||||
<div class="card-cr">
|
||||
{{#if properties.challenge_rating}}
|
||||
CR {{properties.challenge_rating}}
|
||||
{{else if properties.cr}}
|
||||
CR {{properties.cr}}
|
||||
{{else if properties.level}}
|
||||
LVL {{properties.level}}
|
||||
{{else}}
|
||||
CR 1
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Subtitle: Size, Type, Subtype, Alignment -->
|
||||
<div class="card-subtitle">
|
||||
{{#if properties.size}}{{properties.size}}{{else}}medium{{/if}}
|
||||
{{#if properties.type}}{{properties.type}}{{else}}Humanoid{{/if}}{{#if properties.subtype}} ({{properties.subtype}}){{else if properties.race}} ({{properties.race}}){{/if}},
|
||||
{{#if properties.alignment}}{{properties.alignment}}{{else}}unaligned{{/if}}
|
||||
</div>
|
||||
|
||||
<!-- 8 Stat Boxes Row: S, D, C, I, W, Ch, AC, HP -->
|
||||
<div class="stat-boxes-row">
|
||||
<!-- STR -->
|
||||
<div class="stat-box">
|
||||
<span class="badge-advantage">{{advBadge properties "str"}}</span>
|
||||
<span class="badge-proficiency">{{profBadge properties.saving_throws "str"}}</span>
|
||||
<span class="stat-mod">{{modText properties.abilities.strength}}</span>
|
||||
<span class="stat-code">S</span>
|
||||
</div>
|
||||
|
||||
<!-- DEX -->
|
||||
<div class="stat-box">
|
||||
<span class="badge-advantage">{{advBadge properties "dex"}}</span>
|
||||
<span class="badge-proficiency">{{profBadge properties.saving_throws "dex"}}</span>
|
||||
<span class="stat-mod">{{modText properties.abilities.dexterity}}</span>
|
||||
<span class="stat-code">D</span>
|
||||
</div>
|
||||
|
||||
<!-- CON -->
|
||||
<div class="stat-box">
|
||||
<span class="badge-advantage">{{advBadge properties "con"}}</span>
|
||||
<span class="badge-proficiency">{{profBadge properties.saving_throws "con"}}</span>
|
||||
<span class="stat-mod">{{modText properties.abilities.constitution}}</span>
|
||||
<span class="stat-code">C</span>
|
||||
</div>
|
||||
|
||||
<!-- INT -->
|
||||
<div class="stat-box">
|
||||
<span class="badge-advantage">{{advBadge properties "int"}}</span>
|
||||
<span class="badge-proficiency">{{profBadge properties.saving_throws "int"}}</span>
|
||||
<span class="stat-mod">{{modText properties.abilities.intelligence}}</span>
|
||||
<span class="stat-code">I</span>
|
||||
</div>
|
||||
|
||||
<!-- WIS -->
|
||||
<div class="stat-box">
|
||||
<span class="badge-advantage">{{advBadge properties "wis"}}</span>
|
||||
<span class="badge-proficiency">{{profBadge properties.saving_throws "wis"}}</span>
|
||||
<span class="stat-mod">{{modText properties.abilities.wisdom}}</span>
|
||||
<span class="stat-code">W</span>
|
||||
</div>
|
||||
|
||||
<!-- CHA -->
|
||||
<div class="stat-box">
|
||||
<span class="badge-advantage">{{advBadge properties "cha"}}</span>
|
||||
<span class="badge-proficiency">{{profBadge properties.saving_throws "cha"}}</span>
|
||||
<span class="stat-mod">{{modText properties.abilities.charisma}}</span>
|
||||
<span class="stat-code">Ch</span>
|
||||
</div>
|
||||
|
||||
<!-- AC -->
|
||||
<div class="stat-box">
|
||||
<span class="stat-mod">{{val properties.armor_class}}</span>
|
||||
<span class="stat-code">AC</span>
|
||||
</div>
|
||||
|
||||
<!-- HP -->
|
||||
<div class="stat-box">
|
||||
<span class="stat-mod">
|
||||
{{#if properties.hit_points.current}}
|
||||
{{properties.hit_points.current}}
|
||||
{{else if properties.hit_points.max}}
|
||||
{{properties.hit_points.max}}
|
||||
{{else}}
|
||||
{{val properties.hit_points}}
|
||||
{{/if}}
|
||||
</span>
|
||||
<span class="stat-code">HP</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Actions Row: Single Non-Wrapping Row with Horizontal Scroll -->
|
||||
<div class="action-row-container">
|
||||
{{#if properties.actions}}
|
||||
<div class="action-boxes-row">
|
||||
{{#each properties.actions}}
|
||||
<div class="action-card-box">
|
||||
<div class="action-box-title">{{this.name}}</div>
|
||||
<div class="action-box-desc">{{formatText this.desc}}</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="no-actions-box">
|
||||
<span class="no-actions-text">— No listed actions —</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
585
rulesets/open5e/templates/character_sheet.css
Normal file
585
rulesets/open5e/templates/character_sheet.css
Normal file
@@ -0,0 +1,585 @@
|
||||
/* D&D 5e Standard Character Sheet Styles */
|
||||
:root {
|
||||
--border-color: #2b2b2b;
|
||||
--accent-red: #922610;
|
||||
--bg-page: #eaedf0;
|
||||
--bg-sheet: #ffffff;
|
||||
--bg-box: #f9fbfd;
|
||||
--text-main: #1e1e1e;
|
||||
--text-muted: #666666;
|
||||
--font-sans: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
--font-serif: "Libre Baskerville", "Georgia", serif;
|
||||
}
|
||||
|
||||
body.sheet-page-wrapper {
|
||||
margin: 0;
|
||||
padding: 24px;
|
||||
background-color: var(--bg-page);
|
||||
font-family: var(--font-sans);
|
||||
color: var(--text-main);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.character-sheet-container {
|
||||
width: 100%;
|
||||
max-width: 1050px;
|
||||
background: var(--bg-sheet);
|
||||
border: 2px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
padding: 20px 24px 24px;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* ================= HEADER ================= */
|
||||
.sheet-header {
|
||||
display: grid;
|
||||
grid-template-columns: 320px 1fr;
|
||||
gap: 16px;
|
||||
align-items: stretch;
|
||||
border-bottom: 2px solid var(--border-color);
|
||||
padding-bottom: 16px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.header-name-box {
|
||||
border: 1.5px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
padding: 12px 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
background: var(--bg-box);
|
||||
}
|
||||
|
||||
.character-name {
|
||||
font-family: var(--font-serif);
|
||||
font-size: 26px;
|
||||
color: var(--accent-red);
|
||||
margin: 0 0 6px 0;
|
||||
line-height: 1.1;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.header-label {
|
||||
font-size: 9.5px;
|
||||
font-weight: bold;
|
||||
color: var(--text-muted);
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.header-details-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 8px 12px;
|
||||
border: 1.5px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
padding: 10px 14px;
|
||||
background: var(--bg-box);
|
||||
}
|
||||
|
||||
.detail-cell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
|
||||
.detail-val {
|
||||
font-size: 13.5px;
|
||||
font-weight: 600;
|
||||
color: var(--text-main);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
/* ================= 3-COLUMN MAIN GRID ================= */
|
||||
.sheet-main-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 320px 340px 1fr;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.sheet-col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
/* Generic Section Box */
|
||||
.section-box {
|
||||
border: 1.5px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
padding: 10px 12px;
|
||||
background: #ffffff;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.box-bottom-label {
|
||||
text-align: center;
|
||||
font-size: 9.5px;
|
||||
font-weight: bold;
|
||||
color: var(--text-muted);
|
||||
letter-spacing: 0.5px;
|
||||
margin-top: 6px;
|
||||
padding-top: 4px;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
/* ================= LEFT COLUMN ================= */
|
||||
.abilities-and-skills-row {
|
||||
display: grid;
|
||||
grid-template-columns: 88px 1fr;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
/* 6 Abilities */
|
||||
.abilities-stack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.ability-box {
|
||||
border: 1.5px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
padding: 6px 4px 4px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background: var(--bg-box);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ab-title {
|
||||
font-size: 8.5px;
|
||||
font-weight: bold;
|
||||
color: var(--accent-red);
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.ab-modifier {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
color: var(--text-main);
|
||||
margin: 2px 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.ab-score-pill {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
border: 1px solid #999;
|
||||
border-radius: 10px;
|
||||
padding: 1px 8px;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
/* Skills & Saves Stack */
|
||||
.skills-and-saves-stack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.top-stat-pill {
|
||||
border: 1.5px solid var(--border-color);
|
||||
border-radius: 16px;
|
||||
padding: 4px 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background: var(--bg-box);
|
||||
}
|
||||
|
||||
.pill-checkbox {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border: 1.5px solid var(--border-color);
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.pill-checkbox.checked {
|
||||
background-color: var(--accent-red);
|
||||
}
|
||||
|
||||
.pill-value {
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
color: var(--text-main);
|
||||
border-right: 1px solid #ccc;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.pill-label {
|
||||
font-size: 9px;
|
||||
font-weight: bold;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* Proficiency Lists (Saves & Skills) */
|
||||
.prof-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.prof-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 11.5px;
|
||||
margin-bottom: 3.5px;
|
||||
gap: 6px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.prof-item .dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border: 1.2px solid var(--border-color);
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.prof-item .dot.filled {
|
||||
background-color: var(--accent-red);
|
||||
border-color: var(--accent-red);
|
||||
}
|
||||
|
||||
.prof-bonus {
|
||||
font-weight: 700;
|
||||
width: 22px;
|
||||
text-align: right;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.prof-name {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.skill-attr {
|
||||
color: #888;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
/* Passive Perception */
|
||||
.passive-perception-box {
|
||||
border: 1.5px solid var(--border-color);
|
||||
border-radius: 18px;
|
||||
padding: 6px 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
background: var(--bg-box);
|
||||
}
|
||||
|
||||
.passive-score {
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
color: var(--text-main);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 50%;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #ffffff;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.passive-label {
|
||||
font-size: 9.5px;
|
||||
font-weight: bold;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.prof-block-content {
|
||||
font-size: 11.5px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.prof-line {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
/* ================= MIDDLE COLUMN ================= */
|
||||
.combat-top-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.combat-badge {
|
||||
border: 1.5px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
padding: 8px 4px;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--bg-box);
|
||||
}
|
||||
|
||||
.badge-ac {
|
||||
border-color: var(--accent-red);
|
||||
}
|
||||
|
||||
.badge-value {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
color: var(--text-main);
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.badge-label {
|
||||
font-size: 8.5px;
|
||||
font-weight: bold;
|
||||
color: var(--text-muted);
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
/* HP Boxes */
|
||||
.hp-max-line {
|
||||
font-size: 10.5px;
|
||||
color: #666;
|
||||
border-bottom: 1px solid #eee;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.hp-current-val, .temp-hp-val {
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
padding: 8px 0;
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
.hd-and-death-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.half-box {
|
||||
padding: 6px 8px;
|
||||
}
|
||||
|
||||
.hd-total-line {
|
||||
font-size: 9.5px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.hd-current-val {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.death-save-line {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 9px;
|
||||
font-weight: bold;
|
||||
margin: 3px 0;
|
||||
}
|
||||
|
||||
.ds-bubbles {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.ds-bubbles .bubble {
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
border: 1.2px solid var(--border-color);
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.ds-bubbles .bubble.filled {
|
||||
background-color: var(--accent-red);
|
||||
}
|
||||
|
||||
/* Attacks Table */
|
||||
.attacks-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 11px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.attacks-table th {
|
||||
font-size: 8.5px;
|
||||
font-weight: bold;
|
||||
color: var(--text-muted);
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding: 2px 4px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.attacks-table td {
|
||||
padding: 5px 4px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.atk-name {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.atk-bonus {
|
||||
font-weight: 700;
|
||||
color: var(--accent-red);
|
||||
}
|
||||
|
||||
.attack-notes {
|
||||
font-size: 10.5px;
|
||||
line-height: 1.35;
|
||||
color: #444;
|
||||
margin-top: 6px;
|
||||
max-height: 120px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.attack-note-item {
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
/* Equipment */
|
||||
.equipment-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 56px 1fr;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.coins-stack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.coin-row {
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 3px;
|
||||
padding: 2px 4px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 10px;
|
||||
background: var(--bg-box);
|
||||
}
|
||||
|
||||
.coin-tag {
|
||||
font-weight: bold;
|
||||
font-size: 8.5px;
|
||||
color: var(--accent-red);
|
||||
}
|
||||
|
||||
.coin-val {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.items-list-container {
|
||||
max-height: 180px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.items-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.item-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 3px 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.equipped-tag {
|
||||
color: var(--accent-red);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.item-meta {
|
||||
color: #777;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
/* ================= RIGHT COLUMN ================= */
|
||||
.roleplay-box {
|
||||
min-height: 64px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.box-content {
|
||||
font-size: 11.5px;
|
||||
line-height: 1.4;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.box-content p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.empty-hint {
|
||||
color: #999;
|
||||
font-style: italic;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.features-box {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
min-height: 250px;
|
||||
}
|
||||
|
||||
.features-content {
|
||||
font-size: 11.5px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.feature-item {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.feature-name {
|
||||
color: var(--accent-red);
|
||||
display: block;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.feature-desc {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Responsive Styles */
|
||||
@media (max-width: 900px) {
|
||||
.sheet-header {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.sheet-main-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.abilities-and-skills-row {
|
||||
grid-template-columns: 90px 1fr;
|
||||
}
|
||||
}
|
||||
511
rulesets/open5e/templates/character_sheet.html
Normal file
511
rulesets/open5e/templates/character_sheet.html
Normal file
@@ -0,0 +1,511 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<base href="/rulesets/open5e/templates/">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{display_name}} - Character Sheet</title>
|
||||
<link rel="stylesheet" href="character_sheet.css">
|
||||
</head>
|
||||
<body class="sheet-page-wrapper">
|
||||
<div class="character-sheet-container">
|
||||
|
||||
<!-- Top Header Banner -->
|
||||
<header class="sheet-header">
|
||||
<div class="header-name-box">
|
||||
<h1 class="character-name">{{display_name}}</h1>
|
||||
<label class="header-label">CHARACTER NAME</label>
|
||||
</div>
|
||||
<div class="header-details-grid">
|
||||
<div class="detail-cell">
|
||||
<div class="detail-val">{{properties.class}}{{#if properties.subclass}} ({{properties.subclass}}){{/if}} {{properties.level}}</div>
|
||||
<label class="header-label">CLASS & LEVEL</label>
|
||||
</div>
|
||||
<div class="detail-cell">
|
||||
<div class="detail-val">{{#if properties.background}}{{properties.background}}{{else}}—{{/if}}</div>
|
||||
<label class="header-label">BACKGROUND</label>
|
||||
</div>
|
||||
<div class="detail-cell">
|
||||
<div class="detail-val">{{#if properties.player_name}}{{properties.player_name}}{{else}}—{{/if}}</div>
|
||||
<label class="header-label">PLAYER NAME</label>
|
||||
</div>
|
||||
<div class="detail-cell">
|
||||
<div class="detail-val">{{properties.race}}</div>
|
||||
<label class="header-label">RACE</label>
|
||||
</div>
|
||||
<div class="detail-cell">
|
||||
<div class="detail-val">{{properties.alignment}}</div>
|
||||
<label class="header-label">ALIGNMENT</label>
|
||||
</div>
|
||||
<div class="detail-cell">
|
||||
<div class="detail-val">{{#if properties.experience_points}}{{properties.experience_points}}{{else}}0{{/if}} XP</div>
|
||||
<label class="header-label">EXPERIENCE POINTS</label>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Main 3-Column Body -->
|
||||
<main class="sheet-main-grid">
|
||||
|
||||
<!-- ================= LEFT COLUMN ================= -->
|
||||
<section class="sheet-col col-left">
|
||||
|
||||
<div class="abilities-and-skills-row">
|
||||
<!-- 6 Ability Score Boxes -->
|
||||
<div class="abilities-stack">
|
||||
<!-- STR -->
|
||||
<div class="ability-box">
|
||||
<span class="ab-title">STRENGTH</span>
|
||||
<span class="ab-modifier">{{modText properties.abilities.strength}}</span>
|
||||
<div class="ab-score-pill">{{val properties.abilities.strength}}</div>
|
||||
</div>
|
||||
<!-- DEX -->
|
||||
<div class="ability-box">
|
||||
<span class="ab-title">DEXTERITY</span>
|
||||
<span class="ab-modifier">{{modText properties.abilities.dexterity}}</span>
|
||||
<div class="ab-score-pill">{{val properties.abilities.dexterity}}</div>
|
||||
</div>
|
||||
<!-- CON -->
|
||||
<div class="ability-box">
|
||||
<span class="ab-title">CONSTITUTION</span>
|
||||
<span class="ab-modifier">{{modText properties.abilities.constitution}}</span>
|
||||
<div class="ab-score-pill">{{val properties.abilities.constitution}}</div>
|
||||
</div>
|
||||
<!-- INT -->
|
||||
<div class="ability-box">
|
||||
<span class="ab-title">INTELLIGENCE</span>
|
||||
<span class="ab-modifier">{{modText properties.abilities.intelligence}}</span>
|
||||
<div class="ab-score-pill">{{val properties.abilities.intelligence}}</div>
|
||||
</div>
|
||||
<!-- WIS -->
|
||||
<div class="ability-box">
|
||||
<span class="ab-title">WISDOM</span>
|
||||
<span class="ab-modifier">{{modText properties.abilities.wisdom}}</span>
|
||||
<div class="ab-score-pill">{{val properties.abilities.wisdom}}</div>
|
||||
</div>
|
||||
<!-- CHA -->
|
||||
<div class="ability-box">
|
||||
<span class="ab-title">CHARISMA</span>
|
||||
<span class="ab-modifier">{{modText properties.abilities.charisma}}</span>
|
||||
<div class="ab-score-pill">{{val properties.abilities.charisma}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Proficiency, Inspiration, Saves & Skills -->
|
||||
<div class="skills-and-saves-stack">
|
||||
|
||||
<!-- Inspiration & Proficiency Bonus -->
|
||||
<div class="top-stat-pill">
|
||||
<span class="pill-checkbox {{#if properties.inspiration}}checked{{/if}}"></span>
|
||||
<span class="pill-label">INSPIRATION</span>
|
||||
</div>
|
||||
<div class="top-stat-pill">
|
||||
<span class="pill-value">+{{calcProf properties.level properties.cr}}</span>
|
||||
<span class="pill-label">PROFICIENCY BONUS</span>
|
||||
</div>
|
||||
|
||||
<!-- Saving Throws Box -->
|
||||
<div class="section-box saves-box">
|
||||
<ul class="prof-list">
|
||||
<li class="prof-item">
|
||||
<span class="dot {{#if (hasSave properties.saving_throws 'str')}}filled{{/if}}"></span>
|
||||
<span class="prof-bonus">{{saveBonus properties 'strength'}}</span>
|
||||
<span class="prof-name">Strength</span>
|
||||
</li>
|
||||
<li class="prof-item">
|
||||
<span class="dot {{#if (hasSave properties.saving_throws 'dex')}}filled{{/if}}"></span>
|
||||
<span class="prof-bonus">{{saveBonus properties 'dexterity'}}</span>
|
||||
<span class="prof-name">Dexterity</span>
|
||||
</li>
|
||||
<li class="prof-item">
|
||||
<span class="dot {{#if (hasSave properties.saving_throws 'con')}}filled{{/if}}"></span>
|
||||
<span class="prof-bonus">{{saveBonus properties 'constitution'}}</span>
|
||||
<span class="prof-name">Constitution</span>
|
||||
</li>
|
||||
<li class="prof-item">
|
||||
<span class="dot {{#if (hasSave properties.saving_throws 'int')}}filled{{/if}}"></span>
|
||||
<span class="prof-bonus">{{saveBonus properties 'intelligence'}}</span>
|
||||
<span class="prof-name">Intelligence</span>
|
||||
</li>
|
||||
<li class="prof-item">
|
||||
<span class="dot {{#if (hasSave properties.saving_throws 'wis')}}filled{{/if}}"></span>
|
||||
<span class="prof-bonus">{{saveBonus properties 'wisdom'}}</span>
|
||||
<span class="prof-name">Wisdom</span>
|
||||
</li>
|
||||
<li class="prof-item">
|
||||
<span class="dot {{#if (hasSave properties.saving_throws 'cha')}}filled{{/if}}"></span>
|
||||
<span class="prof-bonus">{{saveBonus properties 'charisma'}}</span>
|
||||
<span class="prof-name">Charisma</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="box-bottom-label">SAVING THROWS</div>
|
||||
</div>
|
||||
|
||||
<!-- Skills Box (Alphabetical 18 Skills) -->
|
||||
<div class="section-box skills-box">
|
||||
<ul class="prof-list">
|
||||
<li class="prof-item">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'acrobatics')}}filled{{/if}}"></span>
|
||||
<span class="prof-bonus">{{skillBonus properties 'acrobatics' 'dexterity'}}</span>
|
||||
<span class="prof-name">Acrobatics <small class="skill-attr">(Dex)</small></span>
|
||||
</li>
|
||||
<li class="prof-item">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'animal_handling')}}filled{{/if}}"></span>
|
||||
<span class="prof-bonus">{{skillBonus properties 'animal_handling' 'wisdom'}}</span>
|
||||
<span class="prof-name">Animal Handling <small class="skill-attr">(Wis)</small></span>
|
||||
</li>
|
||||
<li class="prof-item">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'arcana')}}filled{{/if}}"></span>
|
||||
<span class="prof-bonus">{{skillBonus properties 'arcana' 'intelligence'}}</span>
|
||||
<span class="prof-name">Arcana <small class="skill-attr">(Int)</small></span>
|
||||
</li>
|
||||
<li class="prof-item">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'athletics')}}filled{{/if}}"></span>
|
||||
<span class="prof-bonus">{{skillBonus properties 'athletics' 'strength'}}</span>
|
||||
<span class="prof-name">Athletics <small class="skill-attr">(Str)</small></span>
|
||||
</li>
|
||||
<li class="prof-item">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'deception')}}filled{{/if}}"></span>
|
||||
<span class="prof-bonus">{{skillBonus properties 'deception' 'charisma'}}</span>
|
||||
<span class="prof-name">Deception <small class="skill-attr">(Cha)</small></span>
|
||||
</li>
|
||||
<li class="prof-item">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'history')}}filled{{/if}}"></span>
|
||||
<span class="prof-bonus">{{skillBonus properties 'history' 'intelligence'}}</span>
|
||||
<span class="prof-name">History <small class="skill-attr">(Int)</small></span>
|
||||
</li>
|
||||
<li class="prof-item">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'insight')}}filled{{/if}}"></span>
|
||||
<span class="prof-bonus">{{skillBonus properties 'insight' 'wisdom'}}</span>
|
||||
<span class="prof-name">Insight <small class="skill-attr">(Wis)</small></span>
|
||||
</li>
|
||||
<li class="prof-item">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'intimidation')}}filled{{/if}}"></span>
|
||||
<span class="prof-bonus">{{skillBonus properties 'intimidation' 'charisma'}}</span>
|
||||
<span class="prof-name">Intimidation <small class="skill-attr">(Cha)</small></span>
|
||||
</li>
|
||||
<li class="prof-item">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'investigation')}}filled{{/if}}"></span>
|
||||
<span class="prof-bonus">{{skillBonus properties 'investigation' 'intelligence'}}</span>
|
||||
<span class="prof-name">Investigation <small class="skill-attr">(Int)</small></span>
|
||||
</li>
|
||||
<li class="prof-item">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'medicine')}}filled{{/if}}"></span>
|
||||
<span class="prof-bonus">{{skillBonus properties 'medicine' 'wisdom'}}</span>
|
||||
<span class="prof-name">Medicine <small class="skill-attr">(Wis)</small></span>
|
||||
</li>
|
||||
<li class="prof-item">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'nature')}}filled{{/if}}"></span>
|
||||
<span class="prof-bonus">{{skillBonus properties 'nature' 'intelligence'}}</span>
|
||||
<span class="prof-name">Nature <small class="skill-attr">(Int)</small></span>
|
||||
</li>
|
||||
<li class="prof-item">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'perception')}}filled{{/if}}"></span>
|
||||
<span class="prof-bonus">{{skillBonus properties 'perception' 'wisdom'}}</span>
|
||||
<span class="prof-name">Perception <small class="skill-attr">(Wis)</small></span>
|
||||
</li>
|
||||
<li class="prof-item">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'performance')}}filled{{/if}}"></span>
|
||||
<span class="prof-bonus">{{skillBonus properties 'performance' 'charisma'}}</span>
|
||||
<span class="prof-name">Performance <small class="skill-attr">(Cha)</small></span>
|
||||
</li>
|
||||
<li class="prof-item">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'persuasion')}}filled{{/if}}"></span>
|
||||
<span class="prof-bonus">{{skillBonus properties 'persuasion' 'charisma'}}</span>
|
||||
<span class="prof-name">Persuasion <small class="skill-attr">(Cha)</small></span>
|
||||
</li>
|
||||
<li class="prof-item">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'religion')}}filled{{/if}}"></span>
|
||||
<span class="prof-bonus">{{skillBonus properties 'religion' 'intelligence'}}</span>
|
||||
<span class="prof-name">Religion <small class="skill-attr">(Int)</small></span>
|
||||
</li>
|
||||
<li class="prof-item">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'sleight_of_hand')}}filled{{/if}}"></span>
|
||||
<span class="prof-bonus">{{skillBonus properties 'sleight_of_hand' 'dexterity'}}</span>
|
||||
<span class="prof-name">Sleight of Hand <small class="skill-attr">(Dex)</small></span>
|
||||
</li>
|
||||
<li class="prof-item">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'stealth')}}filled{{/if}}"></span>
|
||||
<span class="prof-bonus">{{skillBonus properties 'stealth' 'dexterity'}}</span>
|
||||
<span class="prof-name">Stealth <small class="skill-attr">(Dex)</small></span>
|
||||
</li>
|
||||
<li class="prof-item">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'survival')}}filled{{/if}}"></span>
|
||||
<span class="prof-bonus">{{skillBonus properties 'survival' 'wisdom'}}</span>
|
||||
<span class="prof-name">Survival <small class="skill-attr">(Wis)</small></span>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="box-bottom-label">SKILLS</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Passive Perception Box -->
|
||||
<div class="passive-perception-box">
|
||||
<span class="passive-score">{{passivePerception properties}}</span>
|
||||
<span class="passive-label">PASSIVE WISDOM (PERCEPTION)</span>
|
||||
</div>
|
||||
|
||||
<!-- Other Proficiencies & Languages Box -->
|
||||
<div class="section-box proficiencies-box">
|
||||
<div class="prof-block-content">
|
||||
{{#if properties.other_proficiencies_and_languages.armor}}
|
||||
<div class="prof-line"><strong>Armor:</strong> {{join properties.other_proficiencies_and_languages.armor}}</div>
|
||||
{{/if}}
|
||||
{{#if properties.other_proficiencies_and_languages.weapons}}
|
||||
<div class="prof-line"><strong>Weapons:</strong> {{join properties.other_proficiencies_and_languages.weapons}}</div>
|
||||
{{/if}}
|
||||
{{#if properties.other_proficiencies_and_languages.tools}}
|
||||
<div class="prof-line"><strong>Tools:</strong> {{join properties.other_proficiencies_and_languages.tools}}</div>
|
||||
{{/if}}
|
||||
{{#if properties.languages}}
|
||||
<div class="prof-line"><strong>Languages:</strong> {{join properties.languages}}</div>
|
||||
{{else if properties.other_proficiencies_and_languages.languages}}
|
||||
<div class="prof-line"><strong>Languages:</strong> {{join properties.other_proficiencies_and_languages.languages}}</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="box-bottom-label">OTHER PROFICIENCIES & LANGUAGES</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<!-- ================= MIDDLE COLUMN ================= -->
|
||||
<section class="sheet-col col-middle">
|
||||
|
||||
<!-- Combat Top Row: AC, Initiative, Speed -->
|
||||
<div class="combat-top-row">
|
||||
<div class="combat-badge badge-ac">
|
||||
<span class="badge-value">{{val properties.armor_class}}</span>
|
||||
<span class="badge-label">ARMOR CLASS</span>
|
||||
</div>
|
||||
<div class="combat-badge badge-init">
|
||||
<span class="badge-value">{{#if properties.initiative}}{{modSign properties.initiative}}{{else}}{{modText properties.abilities.dexterity}}{{/if}}</span>
|
||||
<span class="badge-label">INITIATIVE</span>
|
||||
</div>
|
||||
<div class="combat-badge badge-speed">
|
||||
<span class="badge-value">{{#if properties.speed}}{{properties.speed}} ft.{{else}}30 ft.{{/if}}</span>
|
||||
<span class="badge-label">SPEED</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Current Hit Points -->
|
||||
<div class="section-box hp-box">
|
||||
<div class="hp-max-line">
|
||||
Hit Point Maximum: <strong>{{properties.hit_points.max}}</strong>
|
||||
</div>
|
||||
<div class="hp-current-val">
|
||||
{{properties.hit_points.current}}
|
||||
</div>
|
||||
<div class="box-bottom-label">CURRENT HIT POINTS</div>
|
||||
</div>
|
||||
|
||||
<!-- Temporary Hit Points -->
|
||||
<div class="section-box temp-hp-box">
|
||||
<div class="temp-hp-val">
|
||||
{{#if properties.hit_points.temp}}{{properties.hit_points.temp}}{{else}}--{{/if}}
|
||||
</div>
|
||||
<div class="box-bottom-label">TEMPORARY HIT POINTS</div>
|
||||
</div>
|
||||
|
||||
<!-- Hit Dice & Death Saves Row -->
|
||||
<div class="hd-and-death-row">
|
||||
<!-- Hit Dice -->
|
||||
<div class="section-box half-box hd-box">
|
||||
<div class="hd-total-line">Total: {{#if properties.hit_dice.total}}{{properties.hit_dice.total}}{{else}}{{properties.level}}{{/if}}</div>
|
||||
<div class="hd-current-val">
|
||||
{{#if properties.hit_dice.dice}}{{properties.hit_dice.dice}}{{else if properties.hit_points.hit_dice}}{{properties.hit_points.hit_dice}}{{else}}1d10{{/if}}
|
||||
</div>
|
||||
<div class="box-bottom-label">HIT DICE</div>
|
||||
</div>
|
||||
|
||||
<!-- Death Saves -->
|
||||
<div class="section-box half-box death-saves-box">
|
||||
<div class="death-save-line">
|
||||
<span class="ds-title">SUCCESSES</span>
|
||||
<span class="ds-bubbles">
|
||||
<span class="bubble {{#if (gte properties.death_saves.successes 1)}}filled{{/if}}"></span>
|
||||
<span class="bubble {{#if (gte properties.death_saves.successes 2)}}filled{{/if}}"></span>
|
||||
<span class="bubble {{#if (gte properties.death_saves.successes 3)}}filled{{/if}}"></span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="death-save-line">
|
||||
<span class="ds-title">FAILURES</span>
|
||||
<span class="ds-bubbles">
|
||||
<span class="bubble {{#if (gte properties.death_saves.failures 1)}}filled{{/if}}"></span>
|
||||
<span class="bubble {{#if (gte properties.death_saves.failures 2)}}filled{{/if}}"></span>
|
||||
<span class="bubble {{#if (gte properties.death_saves.failures 3)}}filled{{/if}}"></span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="box-bottom-label">DEATH SAVES</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Attacks & Spellcasting -->
|
||||
<div class="section-box attacks-box">
|
||||
<table class="attacks-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>NAME</th>
|
||||
<th>ATK BONUS</th>
|
||||
<th>DAMAGE/TYPE</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#if properties.attacks}}
|
||||
{{#each properties.attacks}}
|
||||
<tr>
|
||||
<td class="atk-name">{{this.name}}</td>
|
||||
<td class="atk-bonus">{{#if this.atk_bonus}}{{this.atk_bonus}}{{else}}+0{{/if}}</td>
|
||||
<td class="atk-damage">{{this.damage}} {{this.damage_type}}</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
{{else if properties.actions}}
|
||||
{{#each properties.actions}}
|
||||
<tr>
|
||||
<td class="atk-name">{{this.name}}</td>
|
||||
<td class="atk-bonus">+5</td>
|
||||
<td class="atk-damage">{{this.desc}}</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
{{else}}
|
||||
<tr><td colspan="3" class="empty-hint">No attacks equipped</td></tr>
|
||||
{{/if}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{{#if properties.actions}}
|
||||
<div class="attack-notes">
|
||||
{{#each properties.actions}}
|
||||
<p class="attack-note-item"><em><strong>{{this.name}}:</strong></em> {{formatText this.desc}}</p>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="box-bottom-label">ATTACKS & SPELLCASTING</div>
|
||||
</div>
|
||||
|
||||
<!-- Equipment & Coins -->
|
||||
<div class="section-box equipment-box">
|
||||
<div class="equipment-layout">
|
||||
<!-- Coins Column -->
|
||||
<div class="coins-stack">
|
||||
<div class="coin-row">
|
||||
<span class="coin-tag">CP</span>
|
||||
<span class="coin-val">{{#if properties.currency.cp}}{{properties.currency.cp}}{{else}}0{{/if}}</span>
|
||||
</div>
|
||||
<div class="coin-row">
|
||||
<span class="coin-tag">SP</span>
|
||||
<span class="coin-val">{{#if properties.currency.sp}}{{properties.currency.sp}}{{else}}0{{/if}}</span>
|
||||
</div>
|
||||
<div class="coin-row">
|
||||
<span class="coin-tag">EP</span>
|
||||
<span class="coin-val">{{#if properties.currency.ep}}{{properties.currency.ep}}{{else}}0{{/if}}</span>
|
||||
</div>
|
||||
<div class="coin-row">
|
||||
<span class="coin-tag">GP</span>
|
||||
<span class="coin-val">{{#if properties.currency.gp}}{{properties.currency.gp}}{{else}}0{{/if}}</span>
|
||||
</div>
|
||||
<div class="coin-row">
|
||||
<span class="coin-tag">PP</span>
|
||||
<span class="coin-val">{{#if properties.currency.pp}}{{properties.currency.pp}}{{else}}0{{/if}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Equipment List -->
|
||||
<div class="items-list-container">
|
||||
<ul class="items-list">
|
||||
{{#each properties.inventory}}
|
||||
<li class="item-row">
|
||||
<span class="item-name">{{this.name}}{{#if this.equipped}} <small class="equipped-tag">(E)</small>{{/if}}</span>
|
||||
<span class="item-meta">x{{this.quantity}}{{#if this.weight}} • {{this.weight}} lb{{/if}}</span>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-bottom-label">EQUIPMENT</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<!-- ================= RIGHT COLUMN ================= -->
|
||||
<section class="sheet-col col-right">
|
||||
|
||||
<!-- Personality Traits -->
|
||||
<div class="section-box roleplay-box">
|
||||
<div class="box-content">
|
||||
{{#if properties.personality_traits}}
|
||||
<p>{{formatText properties.personality_traits}}</p>
|
||||
{{else}}
|
||||
<span class="empty-hint">None</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="box-bottom-label">PERSONALITY TRAITS</div>
|
||||
</div>
|
||||
|
||||
<!-- Ideals -->
|
||||
<div class="section-box roleplay-box">
|
||||
<div class="box-content">
|
||||
{{#if properties.ideals}}
|
||||
<p>{{formatText properties.ideals}}</p>
|
||||
{{else}}
|
||||
<span class="empty-hint">None</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="box-bottom-label">IDEALS</div>
|
||||
</div>
|
||||
|
||||
<!-- Bonds -->
|
||||
<div class="section-box roleplay-box">
|
||||
<div class="box-content">
|
||||
{{#if properties.bonds}}
|
||||
<p>{{formatText properties.bonds}}</p>
|
||||
{{else}}
|
||||
<span class="empty-hint">None</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="box-bottom-label">BONDS</div>
|
||||
</div>
|
||||
|
||||
<!-- Flaws -->
|
||||
<div class="section-box roleplay-box">
|
||||
<div class="box-content">
|
||||
{{#if properties.flaws}}
|
||||
<p>{{formatText properties.flaws}}</p>
|
||||
{{else}}
|
||||
<span class="empty-hint">None</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="box-bottom-label">FLAWS</div>
|
||||
</div>
|
||||
|
||||
<!-- Features & Traits (Large Box) -->
|
||||
<div class="section-box features-box">
|
||||
<div class="features-content">
|
||||
{{#if properties.features_and_traits}}
|
||||
{{#each properties.features_and_traits}}
|
||||
<div class="feature-item">
|
||||
<strong class="feature-name">{{this.name}}</strong>
|
||||
<p class="feature-desc">{{formatText this.desc}}</p>
|
||||
</div>
|
||||
{{/each}}
|
||||
{{else if properties.traits}}
|
||||
{{#each properties.traits}}
|
||||
<div class="feature-item">
|
||||
<strong class="feature-name">{{this.name}}</strong>
|
||||
<p class="feature-desc">{{formatText this.desc}}</p>
|
||||
</div>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="box-bottom-label">FEATURES & TRAITS</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
5
rulesets/open5e/templates/character_sheet.js
Normal file
5
rulesets/open5e/templates/character_sheet.js
Normal file
@@ -0,0 +1,5 @@
|
||||
// Starter client-side script for open5e character sheet
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
console.log('Open5e Character Sheet template initialized.');
|
||||
});
|
||||
|
||||
487
rulesets/open5e/templates/character_sheet_alt.css
Normal file
487
rulesets/open5e/templates/character_sheet_alt.css
Normal file
@@ -0,0 +1,487 @@
|
||||
/* D&D 5e Alternative (Ability-Grouped) Character Sheet Styles */
|
||||
:root {
|
||||
--border-color: #2b2b2b;
|
||||
--accent-red: #922610;
|
||||
--bg-page: #eaedf0;
|
||||
--bg-sheet: #ffffff;
|
||||
--bg-box: #f9fbfd;
|
||||
--text-main: #1e1e1e;
|
||||
--text-muted: #666666;
|
||||
--font-sans: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
--font-serif: "Libre Baskerville", "Georgia", serif;
|
||||
}
|
||||
|
||||
body.sheet-page-wrapper {
|
||||
margin: 0;
|
||||
padding: 24px;
|
||||
background-color: var(--bg-page);
|
||||
font-family: var(--font-sans);
|
||||
color: var(--text-main);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.character-sheet-container {
|
||||
width: 100%;
|
||||
max-width: 1050px;
|
||||
background: var(--bg-sheet);
|
||||
border: 2px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
padding: 20px 24px 24px;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
.sheet-header {
|
||||
display: grid;
|
||||
grid-template-columns: 320px 1fr;
|
||||
gap: 16px;
|
||||
align-items: stretch;
|
||||
border-bottom: 2px solid var(--border-color);
|
||||
padding-bottom: 16px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.header-name-box {
|
||||
border: 1.5px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
padding: 12px 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
background: var(--bg-box);
|
||||
}
|
||||
|
||||
.character-name {
|
||||
font-family: var(--font-serif);
|
||||
font-size: 26px;
|
||||
color: var(--accent-red);
|
||||
margin: 0 0 6px 0;
|
||||
line-height: 1.1;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.header-label {
|
||||
font-size: 9.5px;
|
||||
font-weight: bold;
|
||||
color: var(--text-muted);
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.header-details-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 8px 12px;
|
||||
border: 1.5px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
padding: 10px 14px;
|
||||
background: var(--bg-box);
|
||||
}
|
||||
|
||||
.detail-cell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
|
||||
.detail-val {
|
||||
font-size: 13.5px;
|
||||
font-weight: 600;
|
||||
color: var(--text-main);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
/* 3-Column Grid */
|
||||
.sheet-main-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 320px 340px 1fr;
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.sheet-col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.section-box {
|
||||
border: 1.5px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
padding: 10px 12px;
|
||||
background: #ffffff;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.box-bottom-label {
|
||||
text-align: center;
|
||||
font-size: 9.5px;
|
||||
font-weight: bold;
|
||||
color: var(--text-muted);
|
||||
letter-spacing: 0.5px;
|
||||
margin-top: 6px;
|
||||
padding-top: 4px;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
/* Ability-Grouped Box Styles */
|
||||
.alt-top-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.alt-stat-pill {
|
||||
border: 1.5px solid var(--border-color);
|
||||
border-radius: 16px;
|
||||
padding: 5px 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
background: var(--bg-box);
|
||||
}
|
||||
|
||||
.pill-value {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: var(--accent-red);
|
||||
}
|
||||
|
||||
.pill-checkbox {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border: 1.5px solid var(--border-color);
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.pill-checkbox.checked {
|
||||
background-color: var(--accent-red);
|
||||
}
|
||||
|
||||
.pill-label {
|
||||
font-size: 8.5px;
|
||||
font-weight: bold;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.ability-group-box {
|
||||
border: 1.5px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
padding: 8px 10px;
|
||||
display: grid;
|
||||
grid-template-columns: 78px 1fr;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.ag-score-badge {
|
||||
border: 1.2px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
padding: 4px 2px;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background: var(--bg-box);
|
||||
}
|
||||
|
||||
.ag-title {
|
||||
font-size: 8px;
|
||||
font-weight: bold;
|
||||
color: var(--accent-red);
|
||||
}
|
||||
|
||||
.ag-mod {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: var(--text-main);
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.ag-score {
|
||||
font-size: 10px;
|
||||
color: #555;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 8px;
|
||||
padding: 0 6px;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.ag-sublist {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ag-subitem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 11px;
|
||||
gap: 6px;
|
||||
margin: 2px 0;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.ag-subitem .dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border: 1.2px solid var(--border-color);
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.ag-subitem .dot.filled {
|
||||
background-color: var(--accent-red);
|
||||
border-color: var(--accent-red);
|
||||
}
|
||||
|
||||
.ag-bonus {
|
||||
font-weight: 700;
|
||||
width: 20px;
|
||||
text-align: right;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.ag-name {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* Passive Perception */
|
||||
.passive-perception-box {
|
||||
border: 1.5px solid var(--border-color);
|
||||
border-radius: 18px;
|
||||
padding: 6px 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
background: var(--bg-box);
|
||||
}
|
||||
|
||||
.passive-score {
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
color: var(--text-main);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 50%;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #ffffff;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.passive-label {
|
||||
font-size: 9.5px;
|
||||
font-weight: bold;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* Combat Stats */
|
||||
.combat-top-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.combat-badge {
|
||||
border: 1.5px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
padding: 8px 4px;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--bg-box);
|
||||
}
|
||||
|
||||
.badge-ac { border-color: var(--accent-red); }
|
||||
|
||||
.badge-value {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
color: var(--text-main);
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.badge-label {
|
||||
font-size: 8.5px;
|
||||
font-weight: bold;
|
||||
color: var(--text-muted);
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.hp-max-line {
|
||||
font-size: 10.5px;
|
||||
color: #666;
|
||||
border-bottom: 1px solid #eee;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.hp-current-val, .temp-hp-val {
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
padding: 8px 0;
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
.hd-and-death-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.half-box { padding: 6px 8px; }
|
||||
|
||||
.hd-total-line { font-size: 9.5px; color: #666; }
|
||||
|
||||
.hd-current-val {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.death-save-line {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 9px;
|
||||
font-weight: bold;
|
||||
margin: 3px 0;
|
||||
}
|
||||
|
||||
.ds-bubbles { display: flex; gap: 4px; }
|
||||
|
||||
.ds-bubbles .bubble {
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
border: 1.2px solid var(--border-color);
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.ds-bubbles .bubble.filled { background-color: var(--accent-red); }
|
||||
|
||||
/* Attacks Table */
|
||||
.attacks-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 11px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.attacks-table th {
|
||||
font-size: 8.5px;
|
||||
font-weight: bold;
|
||||
color: var(--text-muted);
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding: 2px 4px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.attacks-table td {
|
||||
padding: 5px 4px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.atk-name { font-weight: 600; }
|
||||
.atk-bonus { font-weight: 700; color: var(--accent-red); }
|
||||
|
||||
.attack-notes {
|
||||
font-size: 10.5px;
|
||||
line-height: 1.35;
|
||||
color: #444;
|
||||
margin-top: 6px;
|
||||
max-height: 120px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.attack-note-item { margin: 4px 0; }
|
||||
|
||||
/* Right Column Roleplay Boxes */
|
||||
.roleplay-box {
|
||||
min-height: 64px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.box-content { font-size: 11.5px; line-height: 1.4; color: #333; }
|
||||
.box-content p { margin: 0; }
|
||||
|
||||
.empty-hint { color: #999; font-style: italic; font-size: 11px; }
|
||||
|
||||
.features-box {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
min-height: 250px;
|
||||
}
|
||||
|
||||
.features-content { font-size: 11.5px; line-height: 1.4; }
|
||||
.feature-item { margin-bottom: 8px; }
|
||||
.feature-name { color: var(--accent-red); display: block; margin-bottom: 2px; }
|
||||
.feature-desc { margin: 0; }
|
||||
|
||||
/* Bottom Wide Row */
|
||||
.sheet-bottom-row {
|
||||
display: grid;
|
||||
grid-template-columns: 320px 1fr;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.prof-block-content { font-size: 11.5px; line-height: 1.45; }
|
||||
.prof-line { margin-bottom: 4px; }
|
||||
|
||||
.equipment-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 56px 1fr;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.coins-stack { display: flex; flex-direction: column; gap: 4px; }
|
||||
|
||||
.coin-row {
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 3px;
|
||||
padding: 2px 4px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 10px;
|
||||
background: var(--bg-box);
|
||||
}
|
||||
|
||||
.coin-tag { font-weight: bold; font-size: 8.5px; color: var(--accent-red); }
|
||||
.coin-val { font-weight: 600; }
|
||||
|
||||
.items-list-container { max-height: 180px; overflow-y: auto; }
|
||||
.items-list { list-style: none; margin: 0; padding: 0; font-size: 11px; }
|
||||
.item-row { display: flex; justify-content: space-between; padding: 3px 0; border-bottom: 1px solid #eee; }
|
||||
.equipped-tag { color: var(--accent-red); font-weight: bold; }
|
||||
.item-meta { color: #777; font-size: 10px; }
|
||||
|
||||
/* Responsive Styles */
|
||||
@media (max-width: 900px) {
|
||||
.sheet-header { grid-template-columns: 1fr; }
|
||||
.sheet-main-grid { grid-template-columns: 1fr; }
|
||||
.sheet-bottom-row { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
472
rulesets/open5e/templates/character_sheet_alt.html
Normal file
472
rulesets/open5e/templates/character_sheet_alt.html
Normal file
@@ -0,0 +1,472 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<base href="/rulesets/open5e/templates/">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{display_name}} - Alternative Character Sheet</title>
|
||||
<link rel="stylesheet" href="character_sheet_alt.css">
|
||||
</head>
|
||||
<body class="sheet-page-wrapper">
|
||||
<div class="character-sheet-container">
|
||||
|
||||
<!-- Top Header Banner -->
|
||||
<header class="sheet-header">
|
||||
<div class="header-name-box">
|
||||
<h1 class="character-name">{{display_name}}</h1>
|
||||
<label class="header-label">CHARACTER NAME</label>
|
||||
</div>
|
||||
<div class="header-details-grid">
|
||||
<div class="detail-cell">
|
||||
<div class="detail-val">{{properties.class}}{{#if properties.subclass}} ({{properties.subclass}}){{/if}} {{properties.level}}</div>
|
||||
<label class="header-label">CLASS & LEVEL</label>
|
||||
</div>
|
||||
<div class="detail-cell">
|
||||
<div class="detail-val">{{#if properties.background}}{{properties.background}}{{else}}—{{/if}}</div>
|
||||
<label class="header-label">BACKGROUND</label>
|
||||
</div>
|
||||
<div class="detail-cell">
|
||||
<div class="detail-val">{{#if properties.player_name}}{{properties.player_name}}{{else}}—{{/if}}</div>
|
||||
<label class="header-label">PLAYER NAME</label>
|
||||
</div>
|
||||
<div class="detail-cell">
|
||||
<div class="detail-val">{{properties.race}}</div>
|
||||
<label class="header-label">RACE</label>
|
||||
</div>
|
||||
<div class="detail-cell">
|
||||
<div class="detail-val">{{properties.alignment}}</div>
|
||||
<label class="header-label">ALIGNMENT</label>
|
||||
</div>
|
||||
<div class="detail-cell">
|
||||
<div class="detail-val">{{#if properties.experience_points}}{{properties.experience_points}}{{else}}0{{/if}} XP</div>
|
||||
<label class="header-label">EXPERIENCE POINTS</label>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Main 3-Column Layout -->
|
||||
<main class="sheet-main-grid">
|
||||
|
||||
<!-- ================= LEFT COLUMN: ABILITY-GROUPED STATS ================= -->
|
||||
<section class="sheet-col col-left">
|
||||
|
||||
<!-- Top Controls: Proficiency Bonus & Inspiration -->
|
||||
<div class="alt-top-row">
|
||||
<div class="alt-stat-pill">
|
||||
<span class="pill-value">+{{calcProf properties.level properties.cr}}</span>
|
||||
<span class="pill-label">PROFICIENCY BONUS</span>
|
||||
</div>
|
||||
<div class="alt-stat-pill">
|
||||
<span class="pill-checkbox {{#if properties.inspiration}}checked{{/if}}"></span>
|
||||
<span class="pill-label">INSPIRATION</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- STRENGTH GROUP -->
|
||||
<div class="ability-group-box">
|
||||
<div class="ag-score-badge">
|
||||
<span class="ag-title">STRENGTH</span>
|
||||
<span class="ag-mod">{{modText properties.abilities.strength}}</span>
|
||||
<span class="ag-score">{{val properties.abilities.strength}}</span>
|
||||
</div>
|
||||
<ul class="ag-sublist">
|
||||
<li class="ag-subitem">
|
||||
<span class="dot {{#if (hasSave properties.saving_throws 'str')}}filled{{/if}}"></span>
|
||||
<span class="ag-bonus">{{saveBonus properties 'strength'}}</span>
|
||||
<span class="ag-name">SAVING THROWS</span>
|
||||
</li>
|
||||
<li class="ag-subitem">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'athletics')}}filled{{/if}}"></span>
|
||||
<span class="ag-bonus">{{skillBonus properties 'athletics' 'strength'}}</span>
|
||||
<span class="ag-name">Athletics</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- DEXTERITY GROUP -->
|
||||
<div class="ability-group-box">
|
||||
<div class="ag-score-badge">
|
||||
<span class="ag-title">DEXTERITY</span>
|
||||
<span class="ag-mod">{{modText properties.abilities.dexterity}}</span>
|
||||
<span class="ag-score">{{val properties.abilities.dexterity}}</span>
|
||||
</div>
|
||||
<ul class="ag-sublist">
|
||||
<li class="ag-subitem">
|
||||
<span class="dot {{#if (hasSave properties.saving_throws 'dex')}}filled{{/if}}"></span>
|
||||
<span class="ag-bonus">{{saveBonus properties 'dexterity'}}</span>
|
||||
<span class="ag-name">SAVING THROWS</span>
|
||||
</li>
|
||||
<li class="ag-subitem">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'acrobatics')}}filled{{/if}}"></span>
|
||||
<span class="ag-bonus">{{skillBonus properties 'acrobatics' 'dexterity'}}</span>
|
||||
<span class="ag-name">Acrobatics</span>
|
||||
</li>
|
||||
<li class="ag-subitem">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'sleight_of_hand')}}filled{{/if}}"></span>
|
||||
<span class="ag-bonus">{{skillBonus properties 'sleight_of_hand' 'dexterity'}}</span>
|
||||
<span class="ag-name">Sleight of Hand</span>
|
||||
</li>
|
||||
<li class="ag-subitem">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'stealth')}}filled{{/if}}"></span>
|
||||
<span class="ag-bonus">{{skillBonus properties 'stealth' 'dexterity'}}</span>
|
||||
<span class="ag-name">Stealth</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- CONSTITUTION GROUP -->
|
||||
<div class="ability-group-box">
|
||||
<div class="ag-score-badge">
|
||||
<span class="ag-title">CONSTITUTION</span>
|
||||
<span class="ag-mod">{{modText properties.abilities.constitution}}</span>
|
||||
<span class="ag-score">{{val properties.abilities.constitution}}</span>
|
||||
</div>
|
||||
<ul class="ag-sublist">
|
||||
<li class="ag-subitem">
|
||||
<span class="dot {{#if (hasSave properties.saving_throws 'con')}}filled{{/if}}"></span>
|
||||
<span class="ag-bonus">{{saveBonus properties 'constitution'}}</span>
|
||||
<span class="ag-name">SAVING THROWS</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- INTELLIGENCE GROUP -->
|
||||
<div class="ability-group-box">
|
||||
<div class="ag-score-badge">
|
||||
<span class="ag-title">INTELLIGENCE</span>
|
||||
<span class="ag-mod">{{modText properties.abilities.intelligence}}</span>
|
||||
<span class="ag-score">{{val properties.abilities.intelligence}}</span>
|
||||
</div>
|
||||
<ul class="ag-sublist">
|
||||
<li class="ag-subitem">
|
||||
<span class="dot {{#if (hasSave properties.saving_throws 'int')}}filled{{/if}}"></span>
|
||||
<span class="ag-bonus">{{saveBonus properties 'intelligence'}}</span>
|
||||
<span class="ag-name">SAVING THROWS</span>
|
||||
</li>
|
||||
<li class="ag-subitem">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'arcana')}}filled{{/if}}"></span>
|
||||
<span class="ag-bonus">{{skillBonus properties 'arcana' 'intelligence'}}</span>
|
||||
<span class="ag-name">Arcana</span>
|
||||
</li>
|
||||
<li class="ag-subitem">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'history')}}filled{{/if}}"></span>
|
||||
<span class="ag-bonus">{{skillBonus properties 'history' 'intelligence'}}</span>
|
||||
<span class="ag-name">History</span>
|
||||
</li>
|
||||
<li class="ag-subitem">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'investigation')}}filled{{/if}}"></span>
|
||||
<span class="ag-bonus">{{skillBonus properties 'investigation' 'intelligence'}}</span>
|
||||
<span class="ag-name">Investigation</span>
|
||||
</li>
|
||||
<li class="ag-subitem">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'nature')}}filled{{/if}}"></span>
|
||||
<span class="ag-bonus">{{skillBonus properties 'nature' 'intelligence'}}</span>
|
||||
<span class="ag-name">Nature</span>
|
||||
</li>
|
||||
<li class="ag-subitem">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'religion')}}filled{{/if}}"></span>
|
||||
<span class="ag-bonus">{{skillBonus properties 'religion' 'intelligence'}}</span>
|
||||
<span class="ag-name">Religion</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- WISDOM GROUP -->
|
||||
<div class="ability-group-box">
|
||||
<div class="ag-score-badge">
|
||||
<span class="ag-title">WISDOM</span>
|
||||
<span class="ag-mod">{{modText properties.abilities.wisdom}}</span>
|
||||
<span class="ag-score">{{val properties.abilities.wisdom}}</span>
|
||||
</div>
|
||||
<ul class="ag-sublist">
|
||||
<li class="ag-subitem">
|
||||
<span class="dot {{#if (hasSave properties.saving_throws 'wis')}}filled{{/if}}"></span>
|
||||
<span class="ag-bonus">{{saveBonus properties 'wisdom'}}</span>
|
||||
<span class="ag-name">SAVING THROWS</span>
|
||||
</li>
|
||||
<li class="ag-subitem">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'animal_handling')}}filled{{/if}}"></span>
|
||||
<span class="ag-bonus">{{skillBonus properties 'animal_handling' 'wisdom'}}</span>
|
||||
<span class="ag-name">Animal Handling</span>
|
||||
</li>
|
||||
<li class="ag-subitem">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'insight')}}filled{{/if}}"></span>
|
||||
<span class="ag-bonus">{{skillBonus properties 'insight' 'wisdom'}}</span>
|
||||
<span class="ag-name">Insight</span>
|
||||
</li>
|
||||
<li class="ag-subitem">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'medicine')}}filled{{/if}}"></span>
|
||||
<span class="ag-bonus">{{skillBonus properties 'medicine' 'wisdom'}}</span>
|
||||
<span class="ag-name">Medicine</span>
|
||||
</li>
|
||||
<li class="ag-subitem">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'perception')}}filled{{/if}}"></span>
|
||||
<span class="ag-bonus">{{skillBonus properties 'perception' 'wisdom'}}</span>
|
||||
<span class="ag-name">Perception</span>
|
||||
</li>
|
||||
<li class="ag-subitem">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'survival')}}filled{{/if}}"></span>
|
||||
<span class="ag-bonus">{{skillBonus properties 'survival' 'wisdom'}}</span>
|
||||
<span class="ag-name">Survival</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- CHARISMA GROUP -->
|
||||
<div class="ability-group-box">
|
||||
<div class="ag-score-badge">
|
||||
<span class="ag-title">CHARISMA</span>
|
||||
<span class="ag-mod">{{modText properties.abilities.charisma}}</span>
|
||||
<span class="ag-score">{{val properties.abilities.charisma}}</span>
|
||||
</div>
|
||||
<ul class="ag-sublist">
|
||||
<li class="ag-subitem">
|
||||
<span class="dot {{#if (hasSave properties.saving_throws 'cha')}}filled{{/if}}"></span>
|
||||
<span class="ag-bonus">{{saveBonus properties 'charisma'}}</span>
|
||||
<span class="ag-name">SAVING THROWS</span>
|
||||
</li>
|
||||
<li class="ag-subitem">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'deception')}}filled{{/if}}"></span>
|
||||
<span class="ag-bonus">{{skillBonus properties 'deception' 'charisma'}}</span>
|
||||
<span class="ag-name">Deception</span>
|
||||
</li>
|
||||
<li class="ag-subitem">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'intimidation')}}filled{{/if}}"></span>
|
||||
<span class="ag-bonus">{{skillBonus properties 'intimidation' 'charisma'}}</span>
|
||||
<span class="ag-name">Intimidation</span>
|
||||
</li>
|
||||
<li class="ag-subitem">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'performance')}}filled{{/if}}"></span>
|
||||
<span class="ag-bonus">{{skillBonus properties 'performance' 'charisma'}}</span>
|
||||
<span class="ag-name">Performance</span>
|
||||
</li>
|
||||
<li class="ag-subitem">
|
||||
<span class="dot {{#if (isSkillProf properties.skills 'persuasion')}}filled{{/if}}"></span>
|
||||
<span class="ag-bonus">{{skillBonus properties 'persuasion' 'charisma'}}</span>
|
||||
<span class="ag-name">Persuasion</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Passive Perception Box -->
|
||||
<div class="passive-perception-box">
|
||||
<span class="passive-score">{{passivePerception properties}}</span>
|
||||
<span class="passive-label">PASSIVE WISDOM (PERCEPTION)</span>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<!-- ================= MIDDLE COLUMN ================= -->
|
||||
<section class="sheet-col col-middle">
|
||||
|
||||
<!-- Combat Top Row -->
|
||||
<div class="combat-top-row">
|
||||
<div class="combat-badge badge-ac">
|
||||
<span class="badge-value">{{val properties.armor_class}}</span>
|
||||
<span class="badge-label">ARMOR CLASS</span>
|
||||
</div>
|
||||
<div class="combat-badge badge-init">
|
||||
<span class="badge-value">{{#if properties.initiative}}{{modSign properties.initiative}}{{else}}{{modText properties.abilities.dexterity}}{{/if}}</span>
|
||||
<span class="badge-label">INITIATIVE</span>
|
||||
</div>
|
||||
<div class="combat-badge badge-speed">
|
||||
<span class="badge-value">{{#if properties.speed}}{{properties.speed}} ft.{{else}}30 ft.{{/if}}</span>
|
||||
<span class="badge-label">SPEED</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- HP Box -->
|
||||
<div class="section-box hp-box">
|
||||
<div class="hp-max-line">
|
||||
Hit Point Maximum: <strong>{{properties.hit_points.max}}</strong>
|
||||
</div>
|
||||
<div class="hp-current-val">{{properties.hit_points.current}}</div>
|
||||
<div class="box-bottom-label">CURRENT HIT POINTS</div>
|
||||
</div>
|
||||
|
||||
<!-- Temp HP -->
|
||||
<div class="section-box temp-hp-box">
|
||||
<div class="temp-hp-val">{{#if properties.hit_points.temp}}{{properties.hit_points.temp}}{{else}}--{{/if}}</div>
|
||||
<div class="box-bottom-label">TEMPORARY HIT POINTS</div>
|
||||
</div>
|
||||
|
||||
<!-- Hit Dice & Death Saves -->
|
||||
<div class="hd-and-death-row">
|
||||
<div class="section-box half-box hd-box">
|
||||
<div class="hd-total-line">Total: {{#if properties.hit_dice.total}}{{properties.hit_dice.total}}{{else}}{{properties.level}}{{/if}}</div>
|
||||
<div class="hd-current-val">
|
||||
{{#if properties.hit_dice.dice}}{{properties.hit_dice.dice}}{{else if properties.hit_points.hit_dice}}{{properties.hit_points.hit_dice}}{{else}}1d10{{/if}}
|
||||
</div>
|
||||
<div class="box-bottom-label">HIT DICE</div>
|
||||
</div>
|
||||
<div class="section-box half-box death-saves-box">
|
||||
<div class="death-save-line">
|
||||
<span class="ds-title">SUCCESSES</span>
|
||||
<span class="ds-bubbles">
|
||||
<span class="bubble {{#if (gte properties.death_saves.successes 1)}}filled{{/if}}"></span>
|
||||
<span class="bubble {{#if (gte properties.death_saves.successes 2)}}filled{{/if}}"></span>
|
||||
<span class="bubble {{#if (gte properties.death_saves.successes 3)}}filled{{/if}}"></span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="death-save-line">
|
||||
<span class="ds-title">FAILURES</span>
|
||||
<span class="ds-bubbles">
|
||||
<span class="bubble {{#if (gte properties.death_saves.failures 1)}}filled{{/if}}"></span>
|
||||
<span class="bubble {{#if (gte properties.death_saves.failures 2)}}filled{{/if}}"></span>
|
||||
<span class="bubble {{#if (gte properties.death_saves.failures 3)}}filled{{/if}}"></span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="box-bottom-label">DEATH SAVES</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Attacks & Spellcasting -->
|
||||
<div class="section-box attacks-box">
|
||||
<table class="attacks-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>NAME</th>
|
||||
<th>ATK BONUS</th>
|
||||
<th>DAMAGE/TYPE</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#if properties.attacks}}
|
||||
{{#each properties.attacks}}
|
||||
<tr>
|
||||
<td class="atk-name">{{this.name}}</td>
|
||||
<td class="atk-bonus">{{#if this.atk_bonus}}{{this.atk_bonus}}{{else}}+0{{/if}}</td>
|
||||
<td class="atk-damage">{{this.damage}} {{this.damage_type}}</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
{{else if properties.actions}}
|
||||
{{#each properties.actions}}
|
||||
<tr>
|
||||
<td class="atk-name">{{this.name}}</td>
|
||||
<td class="atk-bonus">+5</td>
|
||||
<td class="atk-damage">{{this.desc}}</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
{{else}}
|
||||
<tr><td colspan="3" class="empty-hint">No attacks equipped</td></tr>
|
||||
{{/if}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{{#if properties.actions}}
|
||||
<div class="attack-notes">
|
||||
{{#each properties.actions}}
|
||||
<p class="attack-note-item"><em><strong>{{this.name}}:</strong></em> {{formatText this.desc}}</p>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="box-bottom-label">ATTACKS & SPELLCASTING</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<!-- ================= RIGHT COLUMN ================= -->
|
||||
<section class="sheet-col col-right">
|
||||
|
||||
<!-- Personality Traits -->
|
||||
<div class="section-box roleplay-box">
|
||||
<div class="box-content">{{#if properties.personality_traits}}<p>{{formatText properties.personality_traits}}</p>{{else}}<span class="empty-hint">None</span>{{/if}}</div>
|
||||
<div class="box-bottom-label">PERSONALITY TRAITS</div>
|
||||
</div>
|
||||
|
||||
<!-- Ideals -->
|
||||
<div class="section-box roleplay-box">
|
||||
<div class="box-content">{{#if properties.ideals}}<p>{{formatText properties.ideals}}</p>{{else}}<span class="empty-hint">None</span>{{/if}}</div>
|
||||
<div class="box-bottom-label">IDEALS</div>
|
||||
</div>
|
||||
|
||||
<!-- Bonds -->
|
||||
<div class="section-box roleplay-box">
|
||||
<div class="box-content">{{#if properties.bonds}}<p>{{formatText properties.bonds}}</p>{{else}}<span class="empty-hint">None</span>{{/if}}</div>
|
||||
<div class="box-bottom-label">BONDS</div>
|
||||
</div>
|
||||
|
||||
<!-- Flaws -->
|
||||
<div class="section-box roleplay-box">
|
||||
<div class="box-content">{{#if properties.flaws}}<p>{{formatText properties.flaws}}</p>{{else}}<span class="empty-hint">None</span>{{/if}}</div>
|
||||
<div class="box-bottom-label">FLAWS</div>
|
||||
</div>
|
||||
|
||||
<!-- Features & Traits -->
|
||||
<div class="section-box features-box">
|
||||
<div class="features-content">
|
||||
{{#if properties.features_and_traits}}
|
||||
{{#each properties.features_and_traits}}
|
||||
<div class="feature-item">
|
||||
<strong class="feature-name">{{this.name}}</strong>
|
||||
<p class="feature-desc">{{formatText this.desc}}</p>
|
||||
</div>
|
||||
{{/each}}
|
||||
{{else if properties.traits}}
|
||||
{{#each properties.traits}}
|
||||
<div class="feature-item">
|
||||
<strong class="feature-name">{{this.name}}</strong>
|
||||
<p class="feature-desc">{{formatText this.desc}}</p>
|
||||
</div>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="box-bottom-label">FEATURES & TRAITS</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<!-- Bottom Wide Row: Proficiencies & Equipment/Notes -->
|
||||
<div class="sheet-bottom-row">
|
||||
<!-- Other Proficiencies & Languages -->
|
||||
<div class="section-box proficiencies-box">
|
||||
<div class="prof-block-content">
|
||||
{{#if properties.other_proficiencies_and_languages.armor}}
|
||||
<div class="prof-line"><strong>Armor:</strong> {{join properties.other_proficiencies_and_languages.armor}}</div>
|
||||
{{/if}}
|
||||
{{#if properties.other_proficiencies_and_languages.weapons}}
|
||||
<div class="prof-line"><strong>Weapons:</strong> {{join properties.other_proficiencies_and_languages.weapons}}</div>
|
||||
{{/if}}
|
||||
{{#if properties.other_proficiencies_and_languages.tools}}
|
||||
<div class="prof-line"><strong>Tools:</strong> {{join properties.other_proficiencies_and_languages.tools}}</div>
|
||||
{{/if}}
|
||||
{{#if properties.languages}}
|
||||
<div class="prof-line"><strong>Languages:</strong> {{join properties.languages}}</div>
|
||||
{{else if properties.other_proficiencies_and_languages.languages}}
|
||||
<div class="prof-line"><strong>Languages:</strong> {{join properties.other_proficiencies_and_languages.languages}}</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="box-bottom-label">OTHER PROFICIENCIES & LANGUAGES</div>
|
||||
</div>
|
||||
|
||||
<!-- Equipment & Notes -->
|
||||
<div class="section-box equipment-box">
|
||||
<div class="equipment-layout">
|
||||
<div class="coins-stack">
|
||||
<div class="coin-row"><span class="coin-tag">CP</span><span class="coin-val">{{#if properties.currency.cp}}{{properties.currency.cp}}{{else}}0{{/if}}</span></div>
|
||||
<div class="coin-row"><span class="coin-tag">SP</span><span class="coin-val">{{#if properties.currency.sp}}{{properties.currency.sp}}{{else}}0{{/if}}</span></div>
|
||||
<div class="coin-row"><span class="coin-tag">EP</span><span class="coin-val">{{#if properties.currency.ep}}{{properties.currency.ep}}{{else}}0{{/if}}</span></div>
|
||||
<div class="coin-row"><span class="coin-tag">GP</span><span class="coin-val">{{#if properties.currency.gp}}{{properties.currency.gp}}{{else}}0{{/if}}</span></div>
|
||||
<div class="coin-row"><span class="coin-tag">PP</span><span class="coin-val">{{#if properties.currency.pp}}{{properties.currency.pp}}{{else}}0{{/if}}</span></div>
|
||||
</div>
|
||||
<div class="items-list-container">
|
||||
<ul class="items-list">
|
||||
{{#each properties.inventory}}
|
||||
<li class="item-row">
|
||||
<span class="item-name">{{this.name}}{{#if this.equipped}} <small class="equipped-tag">(E)</small>{{/if}}</span>
|
||||
<span class="item-meta">x{{this.quantity}}{{#if this.weight}} • {{this.weight}} lb{{/if}}</span>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-bottom-label">EQUIPMENT & CHARACTER NOTES</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
221
rulesets/open5e/templates/spellcasting_sheet.css
Normal file
221
rulesets/open5e/templates/spellcasting_sheet.css
Normal file
@@ -0,0 +1,221 @@
|
||||
/* D&D 5e Spellcasting Sheet Styles */
|
||||
:root {
|
||||
--border-color: #2b2b2b;
|
||||
--accent-red: #922610;
|
||||
--bg-page: #eaedf0;
|
||||
--bg-sheet: #ffffff;
|
||||
--bg-box: #f9fbfd;
|
||||
--text-main: #1e1e1e;
|
||||
--text-muted: #666666;
|
||||
--font-sans: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
--font-serif: "Libre Baskerville", "Georgia", serif;
|
||||
}
|
||||
|
||||
body.sheet-page-wrapper {
|
||||
margin: 0;
|
||||
padding: 24px;
|
||||
background-color: var(--bg-page);
|
||||
font-family: var(--font-sans);
|
||||
color: var(--text-main);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.spellcasting-sheet-container {
|
||||
width: 100%;
|
||||
max-width: 1050px;
|
||||
background: var(--bg-sheet);
|
||||
border: 2px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
padding: 20px 24px 24px;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
.spell-header {
|
||||
display: grid;
|
||||
grid-template-columns: 320px 1fr;
|
||||
gap: 16px;
|
||||
border-bottom: 2px solid var(--border-color);
|
||||
padding-bottom: 16px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.spell-header-class-box {
|
||||
border: 1.5px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
padding: 12px 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
background: var(--bg-box);
|
||||
}
|
||||
|
||||
.spell-class-name {
|
||||
font-family: var(--font-serif);
|
||||
font-size: 26px;
|
||||
color: var(--accent-red);
|
||||
margin: 0 0 6px 0;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.spell-header-label {
|
||||
font-size: 9px;
|
||||
font-weight: bold;
|
||||
color: var(--text-muted);
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.spell-header-stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 12px;
|
||||
border: 1.5px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
padding: 10px 14px;
|
||||
background: var(--bg-box);
|
||||
}
|
||||
|
||||
.spell-stat-cell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-right: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.spell-stat-cell:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.spell-stat-val {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
color: var(--text-main);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
/* 3-Column Layout */
|
||||
.spells-main-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.spells-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.spell-level-box {
|
||||
border: 1.5px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
background: #ffffff;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.spell-level-header {
|
||||
background: var(--bg-box);
|
||||
border-bottom: 1.5px solid var(--border-color);
|
||||
padding: 6px 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.level-num {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
background: var(--accent-red);
|
||||
border-radius: 50%;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.level-title {
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
color: var(--text-main);
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.spell-level-header.has-slots {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.slot-counters {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
font-size: 10.5px;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.slot-field strong {
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
.spell-items-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 6px 10px;
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
.spell-item-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 4px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.spell-item-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.spell-prep-circle {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border: 1.2px solid var(--border-color);
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.spell-prep-circle.prepared {
|
||||
background: var(--accent-red);
|
||||
border-color: var(--accent-red);
|
||||
}
|
||||
|
||||
.spell-prep-circle.invisible {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.spell-name {
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
.spell-item-row.empty {
|
||||
color: #bbb;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 900px) {
|
||||
.spell-header {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.spells-main-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
277
rulesets/open5e/templates/spellcasting_sheet.html
Normal file
277
rulesets/open5e/templates/spellcasting_sheet.html
Normal file
@@ -0,0 +1,277 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<base href="/rulesets/open5e/templates/">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{display_name}} - Spellcasting Sheet</title>
|
||||
<link rel="stylesheet" href="spellcasting_sheet.css">
|
||||
</head>
|
||||
<body class="sheet-page-wrapper">
|
||||
<div class="spellcasting-sheet-container">
|
||||
|
||||
<!-- Top Spellcasting Header -->
|
||||
<header class="spell-header">
|
||||
<div class="spell-header-class-box">
|
||||
<h1 class="spell-class-name">
|
||||
{{#if properties.spellcasting.class}}
|
||||
{{properties.spellcasting.class}}
|
||||
{{else if properties.class}}
|
||||
{{properties.class}}
|
||||
{{else}}
|
||||
Spellcaster
|
||||
{{/if}}
|
||||
</h1>
|
||||
<label class="spell-header-label">SPELLCASTING CLASS</label>
|
||||
</div>
|
||||
|
||||
<div class="spell-header-stats-grid">
|
||||
<div class="spell-stat-cell">
|
||||
<span class="spell-stat-val">
|
||||
{{#if properties.spellcasting.ability}}
|
||||
{{properties.spellcasting.ability}}
|
||||
{{else}}
|
||||
WISDOM
|
||||
{{/if}}
|
||||
</span>
|
||||
<label class="spell-header-label">SPELLCASTING ABILITY</label>
|
||||
</div>
|
||||
|
||||
<div class="spell-stat-cell">
|
||||
<span class="spell-stat-val">
|
||||
{{#if properties.spellcasting.save_dc}}
|
||||
{{properties.spellcasting.save_dc}}
|
||||
{{else}}
|
||||
13
|
||||
{{/if}}
|
||||
</span>
|
||||
<label class="spell-header-label">SPELL SAVE DC</label>
|
||||
</div>
|
||||
|
||||
<div class="spell-stat-cell">
|
||||
<span class="spell-stat-val">
|
||||
{{#if properties.spellcasting.attack_bonus}}
|
||||
{{modSign properties.spellcasting.attack_bonus}}
|
||||
{{else}}
|
||||
+5
|
||||
{{/if}}
|
||||
</span>
|
||||
<label class="spell-header-label">SPELL ATTACK BONUS</label>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- 3-Column Spells Grid -->
|
||||
<main class="spells-main-grid">
|
||||
|
||||
<!-- ================= COLUMN 1: Cantrips, Level 1, Level 2 ================= -->
|
||||
<section class="spells-column">
|
||||
|
||||
<!-- Cantrips (Level 0) -->
|
||||
<div class="spell-level-box">
|
||||
<div class="spell-level-header">
|
||||
<span class="level-num">0</span>
|
||||
<span class="level-title">CANTRIPS</span>
|
||||
</div>
|
||||
<ul class="spell-items-list">
|
||||
{{#if properties.spellcasting.cantrips}}
|
||||
{{#each properties.spellcasting.cantrips}}
|
||||
<li class="spell-item-row">
|
||||
<span class="spell-prep-circle invisible"></span>
|
||||
<span class="spell-name">{{#if this.name}}{{this.name}}{{else}}{{this}}{{/if}}</span>
|
||||
</li>
|
||||
{{/each}}
|
||||
{{else}}
|
||||
<li class="spell-item-row empty"><span class="spell-name">—</span></li>
|
||||
{{/if}}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Level 1 Spells -->
|
||||
<div class="spell-level-box">
|
||||
<div class="spell-level-header has-slots">
|
||||
<span class="level-num">1</span>
|
||||
<div class="slot-counters">
|
||||
<span class="slot-field">Total: <strong>{{slotTotal ../properties.spellcasting.slots 1}}</strong></span>
|
||||
<span class="slot-field">Expended: <strong>{{slotExpended ../properties.spellcasting.slots 1}}</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="spell-items-list">
|
||||
{{#each (filterSpells properties.spellcasting.spells 1)}}
|
||||
<li class="spell-item-row">
|
||||
<span class="spell-prep-circle {{#if this.prepared}}prepared{{/if}}"></span>
|
||||
<span class="spell-name">{{this.name}}</span>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Level 2 Spells -->
|
||||
<div class="spell-level-box">
|
||||
<div class="spell-level-header has-slots">
|
||||
<span class="level-num">2</span>
|
||||
<div class="slot-counters">
|
||||
<span class="slot-field">Total: <strong>{{slotTotal ../properties.spellcasting.slots 2}}</strong></span>
|
||||
<span class="slot-field">Expended: <strong>{{slotExpended ../properties.spellcasting.slots 2}}</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="spell-items-list">
|
||||
{{#each (filterSpells properties.spellcasting.spells 2)}}
|
||||
<li class="spell-item-row">
|
||||
<span class="spell-prep-circle {{#if this.prepared}}prepared{{/if}}"></span>
|
||||
<span class="spell-name">{{this.name}}</span>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<!-- ================= COLUMN 2: Level 3, Level 4, Level 5 ================= -->
|
||||
<section class="spells-column">
|
||||
|
||||
<!-- Level 3 Spells -->
|
||||
<div class="spell-level-box">
|
||||
<div class="spell-level-header has-slots">
|
||||
<span class="level-num">3</span>
|
||||
<div class="slot-counters">
|
||||
<span class="slot-field">Total: <strong>{{slotTotal ../properties.spellcasting.slots 3}}</strong></span>
|
||||
<span class="slot-field">Expended: <strong>{{slotExpended ../properties.spellcasting.slots 3}}</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="spell-items-list">
|
||||
{{#each (filterSpells properties.spellcasting.spells 3)}}
|
||||
<li class="spell-item-row">
|
||||
<span class="spell-prep-circle {{#if this.prepared}}prepared{{/if}}"></span>
|
||||
<span class="spell-name">{{this.name}}</span>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Level 4 Spells -->
|
||||
<div class="spell-level-box">
|
||||
<div class="spell-level-header has-slots">
|
||||
<span class="level-num">4</span>
|
||||
<div class="slot-counters">
|
||||
<span class="slot-field">Total: <strong>{{slotTotal ../properties.spellcasting.slots 4}}</strong></span>
|
||||
<span class="slot-field">Expended: <strong>{{slotExpended ../properties.spellcasting.slots 4}}</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="spell-items-list">
|
||||
{{#each (filterSpells properties.spellcasting.spells 4)}}
|
||||
<li class="spell-item-row">
|
||||
<span class="spell-prep-circle {{#if this.prepared}}prepared{{/if}}"></span>
|
||||
<span class="spell-name">{{this.name}}</span>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Level 5 Spells -->
|
||||
<div class="spell-level-box">
|
||||
<div class="spell-level-header has-slots">
|
||||
<span class="level-num">5</span>
|
||||
<div class="slot-counters">
|
||||
<span class="slot-field">Total: <strong>{{slotTotal ../properties.spellcasting.slots 5}}</strong></span>
|
||||
<span class="slot-field">Expended: <strong>{{slotExpended ../properties.spellcasting.slots 5}}</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="spell-items-list">
|
||||
{{#each (filterSpells properties.spellcasting.spells 5)}}
|
||||
<li class="spell-item-row">
|
||||
<span class="spell-prep-circle {{#if this.prepared}}prepared{{/if}}"></span>
|
||||
<span class="spell-name">{{this.name}}</span>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<!-- ================= COLUMN 3: Level 6, 7, 8, 9 ================= -->
|
||||
<section class="spells-column">
|
||||
|
||||
<!-- Level 6 Spells -->
|
||||
<div class="spell-level-box">
|
||||
<div class="spell-level-header has-slots">
|
||||
<span class="level-num">6</span>
|
||||
<div class="slot-counters">
|
||||
<span class="slot-field">Total: <strong>{{slotTotal ../properties.spellcasting.slots 6}}</strong></span>
|
||||
<span class="slot-field">Expended: <strong>{{slotExpended ../properties.spellcasting.slots 6}}</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="spell-items-list">
|
||||
{{#each (filterSpells properties.spellcasting.spells 6)}}
|
||||
<li class="spell-item-row">
|
||||
<span class="spell-prep-circle {{#if this.prepared}}prepared{{/if}}"></span>
|
||||
<span class="spell-name">{{this.name}}</span>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Level 7 Spells -->
|
||||
<div class="spell-level-box">
|
||||
<div class="spell-level-header has-slots">
|
||||
<span class="level-num">7</span>
|
||||
<div class="slot-counters">
|
||||
<span class="slot-field">Total: <strong>{{slotTotal ../properties.spellcasting.slots 7}}</strong></span>
|
||||
<span class="slot-field">Expended: <strong>{{slotExpended ../properties.spellcasting.slots 7}}</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="spell-items-list">
|
||||
{{#each (filterSpells properties.spellcasting.spells 7)}}
|
||||
<li class="spell-item-row">
|
||||
<span class="spell-prep-circle {{#if this.prepared}}prepared{{/if}}"></span>
|
||||
<span class="spell-name">{{this.name}}</span>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Level 8 Spells -->
|
||||
<div class="spell-level-box">
|
||||
<div class="spell-level-header has-slots">
|
||||
<span class="level-num">8</span>
|
||||
<div class="slot-counters">
|
||||
<span class="slot-field">Total: <strong>{{slotTotal ../properties.spellcasting.slots 8}}</strong></span>
|
||||
<span class="slot-field">Expended: <strong>{{slotExpended ../properties.spellcasting.slots 8}}</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="spell-items-list">
|
||||
{{#each (filterSpells properties.spellcasting.spells 8)}}
|
||||
<li class="spell-item-row">
|
||||
<span class="spell-prep-circle {{#if this.prepared}}prepared{{/if}}"></span>
|
||||
<span class="spell-name">{{this.name}}</span>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Level 9 Spells -->
|
||||
<div class="spell-level-box">
|
||||
<div class="spell-level-header has-slots">
|
||||
<span class="level-num">9</span>
|
||||
<div class="slot-counters">
|
||||
<span class="slot-field">Total: <strong>{{slotTotal ../properties.spellcasting.slots 9}}</strong></span>
|
||||
<span class="slot-field">Expended: <strong>{{slotExpended ../properties.spellcasting.slots 9}}</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="spell-items-list">
|
||||
{{#each (filterSpells properties.spellcasting.spells 9)}}
|
||||
<li class="spell-item-row">
|
||||
<span class="spell-prep-circle {{#if this.prepared}}prepared{{/if}}"></span>
|
||||
<span class="spell-name">{{this.name}}</span>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
142
rulesets/open5e/templates/stat_block.css
Normal file
142
rulesets/open5e/templates/stat_block.css
Normal file
@@ -0,0 +1,142 @@
|
||||
.stat-block-wrapper {
|
||||
margin: 0;
|
||||
padding: 16px;
|
||||
background-color: #f0f2f5;
|
||||
font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
color: #222;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.stat-block {
|
||||
width: 100%;
|
||||
max-width: 650px;
|
||||
background: #ffffff;
|
||||
padding: 24px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
border-radius: 4px;
|
||||
border-top: 6px solid #922610;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.sb-header {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.sb-name {
|
||||
font-family: "Libre Baskerville", "Georgia", serif;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
color: #922610;
|
||||
margin: 0 0 4px 0;
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
.sb-type {
|
||||
font-style: italic;
|
||||
font-size: 14px;
|
||||
color: #555;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.red-bar {
|
||||
height: 3px;
|
||||
background-color: #922610;
|
||||
margin: 10px 0;
|
||||
border-radius: 1px;
|
||||
}
|
||||
|
||||
.sb-vitals, .sb-meta, .sb-traits {
|
||||
font-size: 14px;
|
||||
line-height: 1.45;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.vital-row, .meta-row {
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.sb-abilities {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
gap: 4px;
|
||||
text-align: center;
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
.sb-ability {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.ab-label {
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
color: #922610;
|
||||
}
|
||||
|
||||
.ab-val {
|
||||
font-size: 13.5px;
|
||||
margin-top: 2px;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.sb-traits {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.trait-block, .action-block {
|
||||
font-size: 14px;
|
||||
line-height: 1.45;
|
||||
margin: 8px 0;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid #eee;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.trait-block:last-child, .action-block:last-child {
|
||||
border-bottom: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.sb-section {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-family: "Libre Baskerville", "Georgia", serif;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
color: #922610;
|
||||
margin: 0 0 4px 0;
|
||||
}
|
||||
|
||||
.section-divider {
|
||||
height: 1.5px;
|
||||
background-color: #922610;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.legendary-preamble {
|
||||
font-size: 13.5px;
|
||||
font-style: italic;
|
||||
line-height: 1.4;
|
||||
margin: 6px 0 10px 0;
|
||||
}
|
||||
|
||||
/* Responsive adjustments for phones */
|
||||
@media (max-width: 480px) {
|
||||
.stat-block-wrapper {
|
||||
padding: 8px;
|
||||
}
|
||||
.stat-block {
|
||||
padding: 14px;
|
||||
}
|
||||
.sb-name {
|
||||
font-size: 24px;
|
||||
}
|
||||
.ab-val {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
234
rulesets/open5e/templates/stat_block.html
Normal file
234
rulesets/open5e/templates/stat_block.html
Normal file
@@ -0,0 +1,234 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{display_name}} - Stat Block</title>
|
||||
<link rel="stylesheet" href="stat_block.css">
|
||||
</head>
|
||||
<body class="stat-block-wrapper">
|
||||
<div class="stat-block">
|
||||
<!-- Header -->
|
||||
<header class="sb-header">
|
||||
<h1 class="sb-name">{{display_name}}</h1>
|
||||
<div class="sb-type">
|
||||
{{#if properties.size}}{{properties.size}}{{else}}Medium{{/if}}
|
||||
{{#if properties.type}}{{properties.type}}{{else}}Humanoid{{/if}}{{#if properties.subtype}} ({{properties.subtype}}){{/if}},
|
||||
{{#if properties.alignment}}{{properties.alignment}}{{else}}unaligned{{/if}}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="red-bar"></div>
|
||||
|
||||
<!-- Core Vitals -->
|
||||
<div class="sb-vitals">
|
||||
<div class="vital-row">
|
||||
<strong>Armor Class</strong> {{val properties.armor_class}}{{#if properties.armor_description}} ({{properties.armor_description}}){{/if}}
|
||||
</div>
|
||||
<div class="vital-row">
|
||||
<strong>Hit Points</strong>
|
||||
{{#if properties.hit_points.current}}
|
||||
{{properties.hit_points.current}}
|
||||
{{else if properties.hit_points.max}}
|
||||
{{properties.hit_points.max}}
|
||||
{{else}}
|
||||
{{val properties.hit_points}}
|
||||
{{/if}}
|
||||
{{#if properties.hit_points.formula}}
|
||||
({{properties.hit_points.formula}})
|
||||
{{else if properties.hit_points.hit_dice}}
|
||||
({{properties.hit_points.hit_dice}})
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="vital-row">
|
||||
<strong>Speed</strong>
|
||||
{{#if properties.speed_desc}}
|
||||
{{properties.speed_desc}}
|
||||
{{else if properties.speed}}
|
||||
{{properties.speed}} ft.
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="red-bar"></div>
|
||||
|
||||
<!-- Ability Scores Table -->
|
||||
<div class="sb-abilities">
|
||||
<div class="sb-ability">
|
||||
<span class="ab-label">STR</span>
|
||||
<span class="ab-val">{{val properties.abilities.strength}} ({{modText properties.abilities.strength}})</span>
|
||||
</div>
|
||||
<div class="sb-ability">
|
||||
<span class="ab-label">DEX</span>
|
||||
<span class="ab-val">{{val properties.abilities.dexterity}} ({{modText properties.abilities.dexterity}})</span>
|
||||
</div>
|
||||
<div class="sb-ability">
|
||||
<span class="ab-label">CON</span>
|
||||
<span class="ab-val">{{val properties.abilities.constitution}} ({{modText properties.abilities.constitution}})</span>
|
||||
</div>
|
||||
<div class="sb-ability">
|
||||
<span class="ab-label">INT</span>
|
||||
<span class="ab-val">{{val properties.abilities.intelligence}} ({{modText properties.abilities.intelligence}})</span>
|
||||
</div>
|
||||
<div class="sb-ability">
|
||||
<span class="ab-label">WIS</span>
|
||||
<span class="ab-val">{{val properties.abilities.wisdom}} ({{modText properties.abilities.wisdom}})</span>
|
||||
</div>
|
||||
<div class="sb-ability">
|
||||
<span class="ab-label">CHA</span>
|
||||
<span class="ab-val">{{val properties.abilities.charisma}} ({{modText properties.abilities.charisma}})</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="red-bar"></div>
|
||||
|
||||
<!-- Optional Monster Meta -->
|
||||
<div class="sb-meta">
|
||||
{{#if properties.saving_throws}}
|
||||
<div class="meta-row">
|
||||
<strong>Saving Throws</strong> {{join properties.saving_throws}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if properties.skills}}
|
||||
<div class="meta-row">
|
||||
<strong>Skills</strong> {{join properties.skills}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if properties.damage_vulnerabilities}}
|
||||
<div class="meta-row">
|
||||
<strong>Damage Vulnerabilities</strong> {{join properties.damage_vulnerabilities}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if properties.damage_resistances}}
|
||||
<div class="meta-row">
|
||||
<strong>Damage Resistances</strong> {{join properties.damage_resistances}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if properties.damage_immunities}}
|
||||
<div class="meta-row">
|
||||
<strong>Damage Immunities</strong> {{join properties.damage_immunities}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if properties.condition_immunities}}
|
||||
<div class="meta-row">
|
||||
<strong>Condition Immunities</strong> {{join properties.condition_immunities}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if properties.senses}}
|
||||
<div class="meta-row">
|
||||
<strong>Senses</strong> {{properties.senses}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if properties.languages}}
|
||||
<div class="meta-row">
|
||||
<strong>Languages</strong> {{join properties.languages}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if properties.challenge_rating}}
|
||||
<div class="meta-row">
|
||||
<strong>Challenge</strong> {{properties.challenge_rating}}
|
||||
</div>
|
||||
{{else if properties.cr}}
|
||||
<div class="meta-row">
|
||||
<strong>Challenge</strong> {{properties.cr}}{{#if properties.xp}} ({{properties.xp}} XP){{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if properties.proficiency_bonus}}
|
||||
<div class="meta-row">
|
||||
<strong>Proficiency Bonus</strong> +{{properties.proficiency_bonus}}
|
||||
</div>
|
||||
{{else if (calcProf properties.level properties.cr)}}
|
||||
<div class="meta-row">
|
||||
<strong>Proficiency Bonus</strong> +{{calcProf properties.level properties.cr}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<!-- Special Traits / Features -->
|
||||
{{#if properties.traits}}
|
||||
<div class="sb-traits">
|
||||
{{#each properties.traits}}
|
||||
<div class="trait-block">
|
||||
<em><strong>{{this.name}}:</strong></em> {{formatText this.desc}}
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<!-- Actions Section -->
|
||||
{{#if properties.actions}}
|
||||
<section class="sb-section">
|
||||
<h2 class="section-title">Actions</h2>
|
||||
<div class="section-divider"></div>
|
||||
{{#each properties.actions}}
|
||||
<div class="action-block">
|
||||
<em><strong>{{this.name}}:</strong></em> {{formatText this.desc}}
|
||||
</div>
|
||||
{{/each}}
|
||||
</section>
|
||||
{{/if}}
|
||||
|
||||
<!-- Bonus Actions Section -->
|
||||
{{#if properties.bonus_actions}}
|
||||
<section class="sb-section">
|
||||
<h2 class="section-title">Bonus Actions</h2>
|
||||
<div class="section-divider"></div>
|
||||
{{#each properties.bonus_actions}}
|
||||
<div class="action-block">
|
||||
<em><strong>{{this.name}}:</strong></em> {{formatText this.desc}}
|
||||
</div>
|
||||
{{/each}}
|
||||
</section>
|
||||
{{/if}}
|
||||
|
||||
<!-- Reactions Section -->
|
||||
{{#if properties.reactions}}
|
||||
<section class="sb-section">
|
||||
<h2 class="section-title">Reactions</h2>
|
||||
<div class="section-divider"></div>
|
||||
{{#each properties.reactions}}
|
||||
<div class="action-block">
|
||||
<em><strong>{{this.name}}:</strong></em> {{formatText this.desc}}
|
||||
</div>
|
||||
{{/each}}
|
||||
</section>
|
||||
{{/if}}
|
||||
|
||||
<!-- Legendary Actions Section -->
|
||||
{{#if properties.legendary_actions}}
|
||||
<section class="sb-section">
|
||||
<h2 class="section-title">Legendary Actions</h2>
|
||||
<div class="section-divider"></div>
|
||||
{{#if properties.legendary_actions.description}}
|
||||
<p class="legendary-preamble">{{formatText properties.legendary_actions.description}}</p>
|
||||
{{else if properties.legendariesDescription}}
|
||||
<p class="legendary-preamble">{{formatText properties.legendariesDescription}}</p>
|
||||
{{/if}}
|
||||
|
||||
{{#if properties.legendary_actions.actions}}
|
||||
{{#each properties.legendary_actions.actions}}
|
||||
<div class="action-block">
|
||||
<em><strong>{{this.name}}:</strong></em> {{formatText this.desc}}
|
||||
</div>
|
||||
{{/each}}
|
||||
{{else if properties.legendaries}}
|
||||
{{#each properties.legendaries}}
|
||||
<div class="action-block">
|
||||
<em><strong>{{this.name}}:</strong></em> {{formatText this.desc}}
|
||||
</div>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
</section>
|
||||
{{/if}}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user