From 10963403e99759e462d652e3277a91aacd975bd5 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Sun, 14 Dec 2025 21:56:49 -0600 Subject: [PATCH] Style abilities tab with ancestry, features, perks, and effects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add ancestry block with header (image, name, type, size) and traits list - Style features and perks lists with grid layout for image/info/description - Add active effects display for temporary and passive effects - Fix perk prerequisites display using getPrerequisiteString() method - Add responsive layout for narrow containers 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- module/sheets/base-actor-sheet.mjs | 6 +- styles/scss/sheets/_actor-sheet.scss | 317 ++++++++++++++++++++++++ templates/actor/character-abilities.hbs | 4 +- 3 files changed, 324 insertions(+), 3 deletions(-) diff --git a/module/sheets/base-actor-sheet.mjs b/module/sheets/base-actor-sheet.mjs index 2291bd0..eb41732 100644 --- a/module/sheets/base-actor-sheet.mjs +++ b/module/sheets/base-actor-sheet.mjs @@ -203,9 +203,13 @@ export default class VagabondActorSheet extends HandlebarsApplicationMixin(Actor case "feature": items.features.push(item); break; - case "perk": + case "perk": { + // Add formatted prerequisite string for display (hide "None") + const prereqStr = item.system.getPrerequisiteString?.() || ""; + item.prerequisiteString = prereqStr !== "None" ? prereqStr : ""; items.perks.push(item); break; + } case "class": items.classes.push(item); break; diff --git a/styles/scss/sheets/_actor-sheet.scss b/styles/scss/sheets/_actor-sheet.scss index 59d2739..80d9e6e 100644 --- a/styles/scss/sheets/_actor-sheet.scss +++ b/styles/scss/sheets/_actor-sheet.scss @@ -1062,6 +1062,323 @@ } } } + + // ---------------------------------------- + // Abilities Tab Layout + // ---------------------------------------- + .abilities-tab { + display: flex; + flex-direction: column; + gap: $spacing-4; + } + + .abilities-section { + .section-header-row { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: $spacing-2; + + .section-header { + margin: 0; + padding-bottom: 0; + border-bottom: none; + } + + .item-create { + @include flex-center; + width: 28px; + height: 28px; + padding: 0; + background-color: $color-parchment; + border: 1px solid $color-border; + border-radius: $radius-md; + color: $color-text-primary; + cursor: pointer; + transition: all $transition-fast; + + &:hover { + background-color: $color-success; + border-color: $color-success; + color: white; + } + + i { + font-size: $font-size-sm; + } + } + } + } + + // Ancestry Block + .ancestry-block { + background-color: $color-parchment-light; + border: 1px solid $color-border-light; + border-radius: $radius-md; + overflow: hidden; + + .ancestry-header { + display: flex; + align-items: center; + gap: $spacing-3; + padding: $spacing-3; + background-color: $color-parchment-dark; + border-bottom: 1px solid $color-border-light; + + .ancestry-img { + width: 48px; + height: 48px; + object-fit: cover; + border: 2px solid $color-border; + border-radius: $radius-md; + background-color: $color-parchment; + } + + .ancestry-info { + display: flex; + flex-direction: column; + gap: $spacing-1; + + .ancestry-name { + font-family: $font-family-header; + font-size: $font-size-lg; + font-weight: $font-weight-bold; + color: $color-text-primary; + cursor: pointer; + + &:hover { + color: $color-accent-primary; + } + } + + .ancestry-being-type, + .ancestry-size { + font-size: $font-size-sm; + color: $color-text-secondary; + } + + .ancestry-being-type::after { + content: " · "; + } + } + } + + .ancestry-traits { + padding: $spacing-3; + + .trait { + padding: $spacing-2 0; + border-bottom: 1px solid $color-border-light; + font-size: $font-size-sm; + line-height: $line-height-relaxed; + + &:last-child { + border-bottom: none; + padding-bottom: 0; + } + + &:first-child { + padding-top: 0; + } + + strong { + color: $color-text-primary; + } + } + } + } + + // Ability Lists (Features & Perks) + .ability-list { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: $spacing-2; + } + + .ability-item { + display: grid; + grid-template-columns: auto 1fr auto; + grid-template-rows: auto auto; + gap: $spacing-2; + padding: $spacing-3; + background-color: $color-parchment-light; + border: 1px solid $color-border-light; + border-radius: $radius-md; + transition: background-color $transition-fast; + + &:hover { + background-color: $color-parchment; + } + + &.empty { + display: flex; + justify-content: center; + padding: $spacing-4; + font-style: italic; + color: $color-text-muted; + } + + .ability-img { + grid-row: span 2; + width: 40px; + height: 40px; + object-fit: cover; + border: 1px solid $color-border; + border-radius: $radius-md; + background-color: $color-parchment; + } + + .ability-info { + display: flex; + flex-direction: column; + gap: 2px; + + .ability-name { + font-weight: $font-weight-semibold; + color: $color-text-primary; + cursor: pointer; + + &:hover { + color: $color-accent-primary; + text-decoration: underline; + } + } + + .ability-source, + .ability-prereqs { + font-size: $font-size-xs; + color: $color-text-secondary; + } + + .ability-prereqs { + font-style: italic; + } + } + + .ability-description { + grid-column: 2 / -1; + font-size: $font-size-sm; + color: $color-text-secondary; + line-height: $line-height-normal; + + // Style inline HTML from descriptions + p { + margin: 0 0 $spacing-2 0; + + &:last-child { + margin-bottom: 0; + } + } + } + + .item-delete { + grid-row: 1; + grid-column: 3; + @include flex-center; + width: 28px; + height: 28px; + padding: 0; + background-color: transparent; + border: 1px solid $color-border-light; + border-radius: $radius-sm; + color: $color-text-muted; + cursor: pointer; + transition: all $transition-fast; + + &:hover { + background-color: rgba($color-danger, 0.1); + border-color: $color-danger; + color: $color-danger; + } + + i { + font-size: $font-size-sm; + } + } + } + + // Active Effects + .effects-group { + margin-bottom: $spacing-3; + + &:last-child { + margin-bottom: 0; + } + } + + .effect-list { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: $spacing-1; + } + + .effect-item { + display: flex; + align-items: center; + gap: $spacing-2; + padding: $spacing-2 $spacing-3; + background-color: $color-parchment-light; + border: 1px solid $color-border-light; + border-radius: $radius-md; + + .effect-icon { + width: 24px; + height: 24px; + object-fit: cover; + border-radius: $radius-sm; + } + + .effect-name { + flex: 1; + font-weight: $font-weight-medium; + color: $color-text-primary; + } + + .effect-duration { + font-size: $font-size-xs; + padding: $spacing-1 $spacing-2; + background-color: rgba($color-warning, 0.15); + border-radius: $radius-sm; + color: $color-warning; + } + + .effect-source { + font-size: $font-size-xs; + color: $color-text-muted; + } + } + + // Responsive abilities layout + @container sheet-content (max-width: 500px) { + .ability-item { + grid-template-columns: auto 1fr; + + .ability-description { + grid-column: 1 / -1; + } + + .item-delete { + position: absolute; + top: $spacing-2; + right: $spacing-2; + } + } + + .ancestry-block .ancestry-header { + flex-direction: column; + text-align: center; + + .ancestry-info { + align-items: center; + } + } + } } // ========================================== diff --git a/templates/actor/character-abilities.hbs b/templates/actor/character-abilities.hbs index 1e2f1f2..5e05180 100644 --- a/templates/actor/character-abilities.hbs +++ b/templates/actor/character-abilities.hbs @@ -66,8 +66,8 @@ {{this.name}}
{{this.name}} - {{#if this.system.prerequisites}} - {{this.system.prerequisites}} + {{#if this.prerequisiteString}} + {{this.prerequisiteString}} {{/if}}
{{{this.system.description}}}