Add light/dark theme system with CSS variables, fix ProseMirror editors

Theme System:
- Add _theme-variables.scss with light (parchment) and dark color palettes
- Register theme options in system.json for Foundry v13 color scheme support
- Convert all SCSS color variables to CSS custom properties
- Update base, mixins, components, and sheet styles for theme support
- Add _applyThemeClass() to actor and item sheet classes

ProseMirror Editor Fix (v13 ApplicationV2):
- Replace {{editor}} helper with <prose-mirror> custom element
- Add TextEditor.enrichHTML() for rich text content preparation
- Update all 8 item templates (spell, weapon, armor, equipment, etc.)
- Fix toolbar hiding content by renaming wrapper to .editor-wrapper
- Style prose-mirror with sticky toolbar and proper flex layout

Roll Dialog & Chat Card Styling:
- Complete roll dialog styling with favor/hinder toggles, info panels
- Complete chat card styling with roll results, damage display, animations
- Mark tasks 5.7 and 5.8 complete in roadmap
- Add task 5.11 for deferred resizable editor feature

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Cal Corum 2025-12-15 15:36:16 -06:00
parent 5ddd33c41c
commit 8b9daa1f36
30 changed files with 1905 additions and 801 deletions

Binary file not shown.

View File

@ -118,6 +118,10 @@ cd ~/.claude/skills/notediscovery && python client.py search "keyword"
Original PDF at: `/mnt/NV2/Development/claude-home/gaming/Vagabond_RPG_-_Pulp_Fantasy_Core_Rulebook_Interactive_PDF.pdf`
Character sheet reference: `/mnt/NV2/Development/claude-home/gaming/Vagabond_-_Hero_Record_Interactive_PDF.pdf`
## Reference Project Documentation
The D&D 5e Foundry system is very well documented here: https://deepwiki.com/foundryvtt/dnd5e/1-dnd5e-system-overview
## Project Roadmap
See `PROJECT_ROADMAP.json` for complete task breakdown with dependencies.

View File

@ -680,28 +680,31 @@
"id": "5.6",
"name": "Style Item sheets",
"description": "Consistent look across all item types, clear form layouts",
"completed": false,
"completed": true,
"tested": false,
"priority": "medium",
"dependencies": ["5.1", "5.2", "5.3", "4.1"]
"dependencies": ["5.1", "5.2", "5.3", "4.1"],
"notes": "Styled all 8 item types with: header (image with hover effect, styled name input, color-coded type badges), tab navigation, form inputs (transitions, focus states, disabled states), fieldsets, properties grids, type-specific sections (traits, prerequisites, progression tables, features, relic sections, spell effects). Added .hidden utility for tab switching, custom scrollbar, flexbox layout for proper scrolling. ALSO IMPLEMENTED: Light/Dark theme system with CSS custom properties - registered themes in system.json, created _theme-variables.scss with light (parchment) and dark color palettes, converted all SCSS color variables to CSS custom properties across base, mixins, components, and sheet styles."
},
{
"id": "5.7",
"name": "Style roll dialogs",
"description": "Favor/Hinder toggles, modifier inputs, clear roll button",
"completed": false,
"completed": true,
"tested": false,
"priority": "medium",
"dependencies": ["5.1", "5.2"]
"dependencies": ["5.1", "5.2"],
"notes": "Comprehensive styling in _roll-dialog.scss (950 lines): ApplicationV2 dialogs with themed backgrounds, skill/weapon/save/spell selections, info panels with difficulty/crit display, favor/hinder toggles with active states, damage preview, mana display for spells, focus warnings, legacy dialog support, Favor/Hinder Debug Panel."
},
{
"id": "5.8",
"name": "Style chat cards",
"description": "Roll results, spell casts, attack outcomes in themed chat messages",
"completed": false,
"completed": true,
"tested": false,
"priority": "medium",
"dependencies": ["5.1", "5.2"]
"dependencies": ["5.1", "5.2"],
"notes": "Comprehensive styling in _chat-cards.scss (577 lines): Card headers with icons and badges, roll results with success/failure/critical/fumble states, roll breakdown display, damage sections with critical highlighting, weapon properties, spell cards with mana cost and focus indicator, animations for crits and fumbles."
},
{
"id": "5.9",
@ -711,6 +714,26 @@
"tested": false,
"priority": "high",
"dependencies": ["5.4", "5.5", "5.6"]
},
{
"id": "5.10",
"name": "Investigate per-sheet theming in Foundry v13",
"description": "Per-sheet theme selection via Configure Sheet dialog doesn't work - Foundry v13 shows theme options but doesn't store selection in document flags or apply theme classes to ApplicationV2 sheets. Infrastructure exists (CSS variables, _applyThemeClass JS), need to find correct Foundry API for per-sheet theme storage. Global theme switching works via Settings > Color Scheme.",
"completed": false,
"tested": false,
"priority": "low",
"dependencies": ["5.6"],
"notes": "CSS infrastructure complete in _theme-variables.scss. JS _applyThemeClass() in base-actor-sheet.mjs and base-item-sheet.mjs applies theme class based on settings. Current workaround: use global Foundry color scheme (Settings > Configure Settings > Color Scheme > Applications). Per-sheet theme dropdown in Configure Sheet appears but selection not persisted to document.flags.core.sheetTheme."
},
{
"id": "5.11",
"name": "Add resizable editor containers",
"description": "Allow users to drag-resize ProseMirror editor text boxes on item sheets for viewing/editing longer content. CSS resize:vertical not working with Foundry's prose-mirror element structure.",
"completed": false,
"tested": false,
"priority": "low",
"dependencies": ["5.6"],
"notes": "Editors functional with prose-mirror element in v13 ApplicationV2. Resize handle not appearing despite CSS - may need JavaScript-based resize or different approach. Foundry's internal .editor-container conflicts with custom wrapper naming."
}
]
},

View File

@ -2,6 +2,9 @@
"VAGABOND.SystemName": "Vagabond RPG",
"VAGABOND.SystemDescription": "A Foundry VTT system for Vagabond RPG - Pulp Fantasy Roleplaying",
"VAGABOND.ThemeLight": "Light (Parchment)",
"VAGABOND.ThemeDark": "Dark",
"VAGABOND.SheetCharacter": "Character Sheet",
"VAGABOND.SheetNPC": "NPC Sheet",
"VAGABOND.SheetItem": "Item Sheet",

View File

@ -44,7 +44,7 @@ export default class VagabondRollDialog extends HandlebarsApplicationMixin(Appli
/** @override */
static DEFAULT_OPTIONS = {
id: "vagabond-roll-dialog",
classes: ["vagabond", "roll-dialog"],
classes: ["vagabond", "roll-dialog", "themed"],
tag: "form",
window: {
title: "VAGABOND.RollDialog",
@ -143,6 +143,9 @@ export default class VagabondRollDialog extends HandlebarsApplicationMixin(Appli
_onRender(context, options) {
super._onRender(context, options);
// Apply theme class based on configured theme
this._applyThemeClass();
// Favor/Hinder toggle buttons
const favorBtn = this.element.querySelector('[data-action="toggle-favor"]');
const hinderBtn = this.element.querySelector('[data-action="toggle-hinder"]');
@ -166,6 +169,40 @@ export default class VagabondRollDialog extends HandlebarsApplicationMixin(Appli
});
}
/**
* Apply the configured theme class to the dialog element.
* Foundry v13 doesn't automatically add theme classes to ApplicationV2,
* so we handle it manually.
* @protected
*/
_applyThemeClass() {
if (!this.element) return;
// Remove any existing theme classes
this.element.classList.remove("theme-light", "theme-dark");
// Check global preference
let theme = null;
try {
const uiConfig = game.settings.get("core", "uiConfig");
const colorScheme = uiConfig?.colorScheme?.applications;
if (colorScheme === "dark") {
theme = "dark";
} else if (colorScheme === "light") {
theme = "light";
}
} catch {
// Settings not available, use default
}
// Apply the theme class
if (theme === "dark") {
this.element.classList.add("theme-dark");
} else if (theme === "light") {
this.element.classList.add("theme-light");
}
}
/**
* Toggle favor on/off.
* @private

View File

@ -26,7 +26,7 @@ export default class FavorHinderDebug extends HandlebarsApplicationMixin(Applica
/** @override */
static DEFAULT_OPTIONS = {
id: "vagabond-favor-hinder-debug",
classes: ["vagabond", "favor-hinder-debug"],
classes: ["vagabond", "favor-hinder-debug", "themed"],
tag: "div",
window: {
title: "Favor/Hinder Debug",
@ -174,6 +174,9 @@ export default class FavorHinderDebug extends HandlebarsApplicationMixin(Applica
_onRender(context, options) {
super._onRender(context, options);
// Apply theme class based on configured theme
this._applyThemeClass();
// Actor selection dropdown
const actorSelect = this.element.querySelector('[name="actorId"]');
actorSelect?.addEventListener("change", (event) => {
@ -319,6 +322,40 @@ export default class FavorHinderDebug extends HandlebarsApplicationMixin(Applica
SkillCheckDialog.prompt(this.actor);
}
/**
* Apply the configured theme class to the dialog element.
* Foundry v13 doesn't automatically add theme classes to ApplicationV2,
* so we handle it manually.
* @protected
*/
_applyThemeClass() {
if (!this.element) return;
// Remove any existing theme classes
this.element.classList.remove("theme-light", "theme-dark");
// Check global preference
let theme = null;
try {
const uiConfig = game.settings.get("core", "uiConfig");
const colorScheme = uiConfig?.colorScheme?.applications;
if (colorScheme === "dark") {
theme = "dark";
} else if (colorScheme === "light") {
theme = "light";
}
} catch {
// Settings not available, use default
}
// Apply the theme class
if (theme === "dark") {
this.element.classList.add("theme-dark");
} else if (theme === "light") {
this.element.classList.add("theme-light");
}
}
/* -------------------------------------------- */
/* Static Methods */
/* -------------------------------------------- */

View File

@ -38,7 +38,7 @@ export default class VagabondActorSheet extends HandlebarsApplicationMixin(Actor
/** @override */
static DEFAULT_OPTIONS = {
id: "vagabond-actor-sheet-{id}",
classes: ["vagabond", "sheet", "actor"],
classes: ["vagabond", "sheet", "actor", "themed"],
tag: "form",
window: {
title: "VAGABOND.ActorSheet",
@ -271,6 +271,9 @@ export default class VagabondActorSheet extends HandlebarsApplicationMixin(Actor
_onRender(context, options) {
super._onRender(context, options);
// Apply theme class based on configured theme
this._applyThemeClass();
// Remove stale tab content (ApplicationV2 appends parts without removing old ones)
this._cleanupInactiveTabs();
@ -284,6 +287,49 @@ export default class VagabondActorSheet extends HandlebarsApplicationMixin(Actor
this._initializeEditors();
}
/**
* Apply the configured theme class to the sheet element.
* Foundry v13 doesn't automatically add theme classes to ApplicationV2 sheets,
* so we handle it manually.
* @protected
*/
_applyThemeClass() {
if (!this.element) return;
// Remove any existing theme classes
this.element.classList.remove("theme-light", "theme-dark");
// Get the configured theme for this sheet
// DocumentSheetConfig stores per-document and per-type theme preferences
const sheetConfig = this.document.getFlag("core", "sheetTheme");
const typeConfig = game.settings.get("core", "sheetClasses")?.[this.document.documentName]?.[
this.document.type
];
const defaultTheme = typeConfig?.defaultTheme;
// Determine which theme to apply: document-specific > type default > global
let theme = sheetConfig || defaultTheme;
// If no specific theme, check global preference
if (!theme) {
const uiConfig = game.settings.get("core", "uiConfig");
const colorScheme = uiConfig?.colorScheme?.applications;
if (colorScheme === "dark") {
theme = "dark";
} else if (colorScheme === "light") {
theme = "light";
}
}
// Apply the theme class
if (theme === "dark") {
this.element.classList.add("theme-dark");
} else if (theme === "light") {
this.element.classList.add("theme-light");
}
// If still no theme, it will use body.theme-dark/light via CSS
}
/**
* Remove tab content sections that don't match the active tab.
* ApplicationV2's parts rendering appends new parts without removing old ones,
@ -581,7 +627,7 @@ export default class VagabondActorSheet extends HandlebarsApplicationMixin(Actor
const weaponId = target.dataset.weaponId;
const { AttackRollDialog } = game.vagabond.applications;
await AttackRollDialog.prompt(this.actor, { weaponId });
await AttackRollDialog.prompt(this.actor, weaponId);
}
/**
@ -594,11 +640,8 @@ export default class VagabondActorSheet extends HandlebarsApplicationMixin(Actor
const spellId = target.dataset.spellId;
if (!spellId) return;
const spell = this.actor.items.get(spellId);
if (!spell) return;
const { SpellCastDialog } = game.vagabond.applications;
await SpellCastDialog.prompt(this.actor, { spell });
await SpellCastDialog.prompt(this.actor, spellId);
}
/**

View File

@ -34,7 +34,7 @@ export default class VagabondItemSheet extends HandlebarsApplicationMixin(ItemSh
/** @override */
static DEFAULT_OPTIONS = {
id: "vagabond-item-sheet-{id}",
classes: ["vagabond", "sheet", "item"],
classes: ["vagabond", "sheet", "item", "themed"],
tag: "form",
window: {
title: "VAGABOND.ItemSheet",
@ -163,6 +163,39 @@ export default class VagabondItemSheet extends HandlebarsApplicationMixin(ItemSh
// Active Effects on this item
context.effects = this._prepareEffects();
// Enrich HTML content for ProseMirror editors
// ApplicationV2 requires enriched HTML to be prepared in context
const enrichOptions = {
secrets: this.item.isOwner,
rollData: context.rollData,
relativeTo: this.item,
};
const TextEditorImpl = foundry.applications.ux.TextEditor.implementation;
context.enrichedDescription = await TextEditorImpl.enrichHTML(
this.item.system.description ?? "",
enrichOptions
);
// Enrich spell-specific fields
if (this.item.type === "spell") {
context.enrichedEffect = await TextEditorImpl.enrichHTML(
this.item.system.effect ?? "",
enrichOptions
);
context.enrichedCritEffect = await TextEditorImpl.enrichHTML(
this.item.system.critEffect ?? "",
enrichOptions
);
}
// Enrich class-specific fields
if (this.item.type === "class") {
context.enrichedStartingPack = await TextEditorImpl.enrichHTML(
this.item.system.startingPack ?? "",
enrichOptions
);
}
// Type-specific context
await this._prepareTypeContext(context, options);
@ -205,6 +238,9 @@ export default class VagabondItemSheet extends HandlebarsApplicationMixin(ItemSh
_onRender(context, options) {
super._onRender(context, options);
// Apply theme class based on configured theme
this._applyThemeClass();
// Clean up inactive tabs if using tabs
if (this.hasTabs) {
this._cleanupInactiveTabs();
@ -214,6 +250,46 @@ export default class VagabondItemSheet extends HandlebarsApplicationMixin(ItemSh
this._initializeEditors();
}
/**
* Apply the configured theme class to the sheet element.
* Foundry v13 doesn't automatically add theme classes to ApplicationV2 sheets,
* so we handle it manually.
* @protected
*/
_applyThemeClass() {
if (!this.element) return;
// Remove any existing theme classes
this.element.classList.remove("theme-light", "theme-dark");
// Try to get per-sheet theme (location TBD - Foundry v13 storage unclear)
// TODO: Find correct API for per-sheet theme in Foundry v13
const sheetConfig = this.document.getFlag("core", "sheetTheme");
const sheetClasses = game.settings.get("core", "sheetClasses");
const typeConfig = sheetClasses?.[this.document.documentName]?.[this.document.type];
// Determine which theme to apply: document-specific > type default > global
let theme = sheetConfig || typeConfig?.defaultTheme;
// If no specific theme, check global preference
if (!theme) {
const uiConfig = game.settings.get("core", "uiConfig");
const colorScheme = uiConfig?.colorScheme?.applications;
if (colorScheme === "dark") {
theme = "dark";
} else if (colorScheme === "light") {
theme = "light";
}
}
// Apply the theme class
if (theme === "dark") {
this.element.classList.add("theme-dark");
} else if (theme === "light") {
this.element.classList.add("theme-light");
}
}
/**
* Remove tab content sections that don't match the active tab.
* ApplicationV2's parts rendering appends new parts without removing old ones.

View File

@ -7,7 +7,7 @@
font-family: $font-family-body;
font-size: $font-size-base;
line-height: $line-height-normal;
color: $color-text-primary;
color: var(--color-text-primary);
// Box sizing
*,
@ -18,12 +18,12 @@
// Links
a {
color: $color-accent-primary;
color: var(--color-accent-primary);
text-decoration: none;
transition: color $transition-fast;
&:hover {
color: $color-accent-secondary;
color: var(--color-accent-secondary);
text-decoration: underline;
}
@ -40,7 +40,7 @@
font-family: $font-family-header;
font-weight: $font-weight-bold;
line-height: $line-height-tight;
color: $color-text-primary;
color: var(--color-text-primary);
margin: 0 0 $spacing-2 0;
}
@ -100,18 +100,18 @@
td {
padding: $spacing-2 $spacing-3;
text-align: left;
border-bottom: 1px solid $color-border-light;
border-bottom: 1px solid var(--color-border-light);
}
th {
font-weight: $font-weight-semibold;
background-color: $color-parchment-dark;
background-color: var(--color-bg-secondary);
}
// Horizontal rule
hr {
border: none;
border-top: 1px solid $color-border;
border-top: 1px solid var(--color-border);
margin: $spacing-4 0;
}
@ -120,7 +120,7 @@
font-family: $font-family-mono;
font-size: $font-size-sm;
padding: $spacing-1 $spacing-2;
background-color: $color-parchment-dark;
background-color: var(--color-bg-secondary);
border-radius: $radius-sm;
}
@ -128,48 +128,41 @@
blockquote {
margin: 0 0 $spacing-4 0;
padding: $spacing-3 $spacing-4;
border-left: 3px solid $color-accent-primary;
background-color: $color-parchment-dark;
border-left: 3px solid var(--color-accent-primary);
background-color: var(--color-bg-secondary);
font-style: italic;
}
// Selection
::selection {
background-color: $color-accent-highlight;
color: $color-text-primary;
background-color: var(--color-accent-highlight);
color: var(--color-text-primary);
}
}
// Foundry sheet wrapper styles
.sheet.vagabond {
@include custom-scrollbar;
background: $color-parchment;
background-image: linear-gradient(
to bottom,
rgba($color-parchment-light, 0.3) 0%,
transparent 5%,
transparent 95%,
rgba($color-parchment-dark, 0.3) 100%
);
background: var(--color-bg-primary);
}
// Window header customization
.window-app.vagabond {
.window-header {
background: linear-gradient(to bottom, $color-parchment-dark, $color-parchment-darker);
border-bottom: 2px solid $color-border-dark;
background: linear-gradient(to bottom, var(--color-bg-secondary), var(--color-bg-tertiary));
border-bottom: 2px solid var(--color-border-dark);
.window-title {
font-family: $font-family-header;
font-size: $font-size-lg;
color: $color-text-primary;
color: var(--color-text-primary);
}
.header-button {
color: $color-text-secondary;
color: var(--color-text-secondary);
&:hover {
color: $color-text-primary;
color: var(--color-text-primary);
}
}
}

View File

@ -32,7 +32,7 @@
font-size: $size;
font-weight: $font-weight-bold;
line-height: $line-height-tight;
color: $color-text-primary;
color: var(--color-text-primary);
}
@mixin body-text($size: $font-size-base) {
@ -40,7 +40,7 @@
font-size: $size;
font-weight: $font-weight-normal;
line-height: $line-height-normal;
color: $color-text-primary;
color: var(--color-text-primary);
}
// Buttons
@ -60,7 +60,7 @@
transition: all $transition-fast;
&:focus {
outline: 2px solid $color-accent-primary;
outline: 2px solid var(--color-accent-primary);
outline-offset: 2px;
}
@ -72,23 +72,23 @@
@mixin button-primary {
@include button-base;
background-color: $color-accent-primary;
color: $color-text-inverse;
border-color: $color-accent-secondary;
background-color: var(--color-accent-primary);
color: var(--color-text-inverse);
border-color: var(--color-accent-secondary);
&:hover:not(:disabled) {
background-color: $color-accent-secondary;
background-color: var(--color-accent-secondary);
}
}
@mixin button-secondary {
@include button-base;
background-color: transparent;
color: $color-text-primary;
border-color: $color-border;
color: var(--color-text-primary);
border-color: var(--color-border);
&:hover:not(:disabled) {
background-color: $color-parchment-dark;
background-color: var(--color-bg-secondary);
}
}
@ -100,7 +100,7 @@
height: 28px;
padding: 0;
font-size: $font-size-sm;
color: $color-text-secondary;
color: var(--color-text-secondary);
background-color: transparent;
border: 1px solid transparent;
border-radius: $radius-sm;
@ -108,13 +108,13 @@
transition: all $transition-fast;
&:hover:not(:disabled) {
color: $color-text-primary;
background-color: $color-parchment-dark;
border-color: $color-border;
color: var(--color-text-primary);
background-color: var(--color-bg-secondary);
border-color: var(--color-border);
}
&:focus {
outline: 2px solid $color-accent-primary;
outline: 2px solid var(--color-accent-primary);
outline-offset: 2px;
}
@ -129,41 +129,41 @@
padding: $spacing-2 $spacing-3;
font-family: $font-family-body;
font-size: $font-size-base;
color: $color-text-primary;
background-color: $color-parchment-light;
border: 1px solid $color-border;
color: var(--color-text-primary);
background-color: var(--color-bg-input);
border: 1px solid var(--color-border);
border-radius: $radius-md;
transition: border-color $transition-fast;
&:focus {
outline: none;
border-color: $color-accent-primary;
box-shadow: 0 0 0 2px rgba($color-accent-primary, 0.2);
border-color: var(--color-accent-primary);
box-shadow: 0 0 0 2px rgba(139, 69, 19, 0.2); // Using fixed color for box-shadow
}
&::placeholder {
color: $color-text-muted;
color: var(--color-text-muted);
}
&:disabled {
background-color: $color-parchment-darker;
background-color: var(--color-bg-tertiary);
cursor: not-allowed;
}
}
// Cards/Panels
@mixin panel {
background-color: $color-parchment;
border: 1px solid $color-border;
background-color: var(--color-bg-primary);
border: 1px solid var(--color-border);
border-radius: $radius-md;
box-shadow: 0 1px 3px $shadow-light;
box-shadow: 0 1px 3px var(--shadow-light);
}
@mixin panel-header {
@include flex-between;
padding: $spacing-3 $spacing-4;
background-color: $color-parchment-dark;
border-bottom: 1px solid $color-border;
background-color: var(--color-bg-secondary);
border-bottom: 1px solid var(--color-border);
border-radius: $radius-md $radius-md 0 0;
}
@ -175,16 +175,16 @@
}
&::-webkit-scrollbar-track {
background: $color-parchment-dark;
background: var(--color-bg-secondary);
border-radius: $radius-full;
}
&::-webkit-scrollbar-thumb {
background: $color-border;
background: var(--color-border);
border-radius: $radius-full;
&:hover {
background: $color-border-dark;
background: var(--color-border-dark);
}
}
}
@ -192,7 +192,7 @@
// Accessibility - focus visible
@mixin focus-visible {
&:focus-visible {
outline: 2px solid $color-accent-primary;
outline: 2px solid var(--color-accent-primary);
outline-offset: 2px;
}
}
@ -218,7 +218,7 @@
}
// Stat badge (for the large stat numbers)
@mixin stat-badge($color: $color-text-primary) {
@mixin stat-badge($color: var(--color-text-primary)) {
@include flex-center;
width: 3rem;
height: 3rem;
@ -226,7 +226,7 @@
font-size: $font-size-3xl;
font-weight: $font-weight-bold;
color: $color;
background-color: $color-parchment-light;
border: 2px solid $color-border;
background-color: var(--color-bg-input);
border: 2px solid var(--color-border);
border-radius: $radius-md;
}

View File

@ -0,0 +1,146 @@
// Vagabond RPG - Theme Variables (CSS Custom Properties)
// ======================================================
// These CSS custom properties enable theme switching.
// The SCSS variables are kept for compile-time calculations (like mix()).
// Runtime theming uses the CSS custom properties.
// ==========================================
// LIGHT THEME (Default - Parchment)
// ==========================================
// This is the default theme, warm parchment colors
// WCAG AA compliant (4.5:1 contrast ratio minimum)
.vagabond {
// Background colors
--color-bg-primary: #{$color-parchment};
--color-bg-secondary: #{$color-parchment-dark};
--color-bg-tertiary: #{$color-parchment-darker};
--color-bg-input: #{$color-parchment-light};
--color-bg-highlight: #{$color-parchment-light};
// Text colors
--color-text-primary: #{$color-text-primary};
--color-text-secondary: #{$color-text-secondary};
--color-text-muted: #{$color-text-muted};
--color-text-inverse: #{$color-text-inverse};
// Accent colors
--color-accent-primary: #{$color-accent-primary};
--color-accent-secondary: #{$color-accent-secondary};
--color-accent-highlight: #{$color-accent-highlight};
// Semantic colors
--color-success: #{$color-success};
--color-danger: #{$color-danger};
--color-warning: #{$color-warning};
--color-info: #{$color-info};
// Stat colors (for visual distinction)
--color-might: #{$color-might};
--color-dexterity: #{$color-dexterity};
--color-awareness: #{$color-awareness};
--color-reason: #{$color-reason};
--color-presence: #{$color-presence};
--color-luck: #{$color-luck};
// Border colors
--color-border: #{$color-border};
--color-border-light: #{$color-border-light};
--color-border-dark: #{$color-border-dark};
// Shadows
--shadow-light: #{$shadow-light};
--shadow-medium: #{$shadow-medium};
--shadow-dark: #{$shadow-dark};
// Resource bar colors (HP, Mana)
--color-hp-fill: #{$color-danger};
--color-hp-bg: #{$color-parchment-darker};
--color-mana-fill: #{$color-reason};
--color-mana-bg: #{$color-parchment-darker};
--color-bar-values-bg: rgba(245, 240, 225, 0.85);
--color-bar-input-bg: rgba(255, 255, 255, 0.9);
// Type badge backgrounds (mixed with background)
--color-badge-weapon: #{mix($color-danger, $color-parchment, 15%)};
--color-badge-armor: #{mix($color-info, $color-parchment, 15%)};
--color-badge-spell: #{mix($color-reason, $color-parchment, 15%)};
--color-badge-perk: #{mix($color-success, $color-parchment, 15%)};
--color-badge-class: #{mix($color-warning, $color-parchment, 15%)};
--color-badge-feature: #{mix($color-presence, $color-parchment, 15%)};
--color-badge-ancestry: #{mix($color-luck, $color-parchment, 15%)};
--color-badge-equipment: #{mix($color-dexterity, $color-parchment, 15%)};
}
// ==========================================
// DARK THEME
// ==========================================
// Dark backgrounds with warm parchment-tinted text
// Maintains the fantasy aesthetic while being easier on eyes
//
// Two application methods:
// 1. Global: body.theme-dark .vagabond - when Foundry's global theme is dark
// 2. Per-sheet: .vagabond.theme-dark - when sheet has theme-dark class (applied by _applyThemeClass)
body.theme-dark .vagabond:not(.theme-light),
.vagabond.theme-dark {
// Dark background colors (warm-tinted darks)
--color-bg-primary: #1e1a16;
--color-bg-secondary: #2a2520;
--color-bg-tertiary: #36302a;
--color-bg-input: #252119;
--color-bg-highlight: #3d362e;
// Light text colors (parchment-tinted)
--color-text-primary: #e8dcc8;
--color-text-secondary: #c4b8a4;
--color-text-muted: #8a7e6e;
--color-text-inverse: #1e1a16;
// Accent colors (slightly brighter for dark bg)
--color-accent-primary: #c9682a;
--color-accent-secondary: #a85a24;
--color-accent-highlight: #e8a060;
// Semantic colors (adjusted for dark backgrounds)
--color-success: #4a9f42;
--color-danger: #c94444;
--color-warning: #d4a32c;
--color-info: #4a8080;
// Stat colors (brighter for dark bg)
--color-might: #e05050;
--color-dexterity: #50c050;
--color-awareness: #6090e0;
--color-reason: #b060e0;
--color-presence: #e0b030;
--color-luck: #40d0d0;
// Border colors (warm grays)
--color-border: #5c4a3a;
--color-border-light: #6d5a4a;
--color-border-dark: #4a3a2a;
// Shadows (more pronounced on dark)
--shadow-light: rgba(0, 0, 0, 0.2);
--shadow-medium: rgba(0, 0, 0, 0.35);
--shadow-dark: rgba(0, 0, 0, 0.5);
// Resource bar colors
--color-hp-fill: #c94444;
--color-hp-bg: #2a2520;
--color-mana-fill: #7050b0;
--color-mana-bg: #2a2520;
--color-bar-values-bg: rgba(30, 26, 22, 0.9);
--color-bar-input-bg: rgba(42, 37, 32, 0.95);
// Type badge backgrounds (for dark theme)
--color-badge-weapon: #3d2828;
--color-badge-armor: #283838;
--color-badge-spell: #382840;
--color-badge-perk: #283828;
--color-badge-class: #383828;
--color-badge-feature: #383028;
--color-badge-ancestry: #283838;
--color-badge-equipment: #283828;
}

View File

@ -1,36 +1,45 @@
// Vagabond RPG - Chat Card Styles
// ================================
// Uses CSS custom properties for theme support.
// Base chat card
.vagabond.chat-card {
@include panel;
overflow: hidden;
background-color: var(--color-bg-primary);
// Card header
.card-header {
@include flex-between;
padding: $spacing-2 $spacing-3;
background-color: $color-parchment-dark;
border-bottom: 1px solid $color-border;
background-color: var(--color-bg-secondary);
border-bottom: 1px solid var(--color-border);
.card-title,
h3 {
@include flex-center;
gap: $spacing-2;
font-family: $font-family-header;
font-size: $font-size-base;
font-weight: $font-weight-bold;
margin: 0;
color: var(--color-text-primary);
i {
color: var(--color-accent-primary);
}
}
.card-subtitle {
font-size: $font-size-sm;
color: $color-text-muted;
color: var(--color-text-muted);
}
.trained-badge {
font-size: $font-size-xs;
padding: $spacing-1 $spacing-2;
background-color: rgba($color-success, 0.2);
color: $color-success;
background-color: rgba(74, 159, 66, 0.2);
color: var(--color-success);
border-radius: $radius-full;
font-weight: $font-weight-medium;
}
@ -46,17 +55,18 @@
@include flex-column;
align-items: center;
gap: $spacing-2;
padding: $spacing-3;
margin: $spacing-2 0;
background-color: $color-parchment-light;
padding: $spacing-4;
margin: $spacing-3;
background-color: var(--color-bg-secondary);
border-radius: $radius-md;
border: 2px solid $color-border;
border: 2px solid var(--color-border);
.roll-total {
font-family: $font-family-header;
font-size: $font-size-4xl;
font-weight: $font-weight-bold;
line-height: 1;
color: var(--color-text-primary);
}
.roll-status {
@ -71,45 +81,45 @@
}
.success {
background-color: rgba($color-success, 0.2);
color: $color-success;
background-color: rgba(74, 159, 66, 0.2);
color: var(--color-success);
}
.failure {
background-color: rgba($color-danger, 0.2);
color: $color-danger;
background-color: rgba(201, 68, 68, 0.2);
color: var(--color-danger);
}
.critical {
background-color: rgba($color-warning, 0.3);
color: $color-warning;
background-color: rgba(212, 163, 44, 0.3);
color: var(--color-warning);
animation: pulse 1s ease-in-out;
}
.fumble {
background-color: rgba($color-danger, 0.3);
color: $color-danger;
background-color: rgba(201, 68, 68, 0.3);
color: var(--color-danger);
animation: shake 0.5s ease-in-out;
}
}
// Conditional styling based on result
&.success {
border-color: $color-success;
border-color: var(--color-success);
}
&.failure {
border-color: $color-danger;
border-color: var(--color-danger);
}
&.critical {
border-color: $color-warning;
background-color: rgba($color-warning, 0.1);
border-color: var(--color-warning);
background-color: rgba(212, 163, 44, 0.1);
}
&.fumble {
border-color: $color-danger;
background-color: rgba($color-danger, 0.1);
border-color: var(--color-danger);
background-color: rgba(201, 68, 68, 0.1);
}
}
@ -117,8 +127,9 @@
.roll-details {
@include flex-column;
gap: $spacing-2;
padding: $spacing-2;
background-color: $color-parchment-dark;
padding: $spacing-3;
margin: 0 $spacing-3 $spacing-3;
background-color: var(--color-bg-secondary);
border-radius: $radius-md;
font-size: $font-size-sm;
@ -126,11 +137,12 @@
@include flex-between;
.label {
color: $color-text-muted;
color: var(--color-text-muted);
}
.value {
font-family: $font-family-mono;
color: var(--color-text-primary);
}
}
@ -138,7 +150,7 @@
@include flex-center;
gap: $spacing-3;
padding-top: $spacing-2;
border-top: 1px solid $color-border;
border-top: 1px solid var(--color-border);
span {
@include flex-center;
@ -148,32 +160,38 @@
.d20-result {
font-family: $font-family-mono;
font-weight: $font-weight-bold;
color: var(--color-text-primary);
}
.favor-die {
font-family: $font-family-mono;
&.favor {
color: $color-success;
color: var(--color-success);
}
&.hinder {
color: $color-danger;
color: var(--color-danger);
}
}
.modifier {
font-family: $font-family-mono;
color: $color-text-secondary;
color: var(--color-text-secondary);
}
}
}
// Target info (difficulty, crit threshold)
// Info sections (save-info, skill-info, target-info)
.save-info,
.skill-info,
.target-info {
@include grid(2, $spacing-2);
margin-top: $spacing-2;
padding: $spacing-2;
@include flex-column;
gap: $spacing-1;
margin: 0 $spacing-3 $spacing-3;
padding: $spacing-2 $spacing-3;
background-color: var(--color-bg-tertiary);
border-radius: $radius-md;
font-size: $font-size-sm;
> div {
@ -181,11 +199,53 @@
}
.label {
color: $color-text-muted;
color: var(--color-text-muted);
}
.value {
font-weight: $font-weight-medium;
color: var(--color-text-primary);
}
.stat-abbr {
font-weight: $font-weight-bold;
color: var(--color-accent-primary);
}
}
// Defense info display
.defense-info-display {
@include flex-center;
gap: $spacing-2;
margin: 0 $spacing-3 $spacing-3;
padding: $spacing-2 $spacing-3;
background-color: rgba(64, 144, 224, 0.1);
border: 1px solid var(--color-info);
border-radius: $radius-md;
font-size: $font-size-sm;
color: var(--color-info);
i {
font-size: $font-size-base;
}
}
// Defense badge
.defense-badge {
font-size: $font-size-xs;
padding: $spacing-1 $spacing-2;
border-radius: $radius-full;
font-weight: $font-weight-medium;
text-transform: uppercase;
&.block {
background-color: rgba(64, 144, 224, 0.2);
color: var(--color-info);
}
&.dodge {
background-color: rgba(74, 159, 66, 0.2);
color: var(--color-success);
}
}
@ -194,20 +254,20 @@
.hinder-sources {
@include flex-center;
gap: $spacing-2;
margin-top: $spacing-2;
margin: 0 $spacing-3 $spacing-3;
padding: $spacing-2;
font-size: $font-size-sm;
border-radius: $radius-md;
}
.favor-sources {
background-color: rgba($color-success, 0.1);
color: $color-success;
background-color: rgba(74, 159, 66, 0.1);
color: var(--color-success);
}
.hinder-sources {
background-color: rgba($color-danger, 0.1);
color: $color-danger;
background-color: rgba(201, 68, 68, 0.1);
color: var(--color-danger);
}
// Result status (legacy)
@ -220,18 +280,18 @@
border-radius: $radius-md;
&.success {
background-color: rgba($color-success, 0.2);
color: $color-success;
background-color: rgba(74, 159, 66, 0.2);
color: var(--color-success);
}
&.failure {
background-color: rgba($color-danger, 0.2);
color: $color-danger;
background-color: rgba(201, 68, 68, 0.2);
color: var(--color-danger);
}
&.critical {
background-color: rgba($color-warning, 0.2);
color: $color-warning;
background-color: rgba(212, 163, 44, 0.2);
color: var(--color-warning);
animation: pulse 1s ease-in-out;
}
}
@ -240,27 +300,27 @@
.damage-display {
@include flex-center;
gap: $spacing-2;
margin-top: $spacing-3;
margin: $spacing-3;
padding: $spacing-2;
background-color: rgba($color-danger, 0.1);
border: 1px solid $color-danger;
background-color: rgba(201, 68, 68, 0.1);
border: 1px solid var(--color-danger);
border-radius: $radius-md;
.damage-label {
font-size: $font-size-sm;
color: $color-text-secondary;
color: var(--color-text-secondary);
}
.damage-value {
font-family: $font-family-header;
font-size: $font-size-xl;
font-weight: $font-weight-bold;
color: $color-danger;
color: var(--color-danger);
}
.damage-type {
font-size: $font-size-sm;
color: $color-text-muted;
color: var(--color-text-muted);
}
}
@ -269,7 +329,7 @@
display: flex;
gap: $spacing-2;
padding: $spacing-3;
border-top: 1px solid $color-border;
border-top: 1px solid var(--color-border);
button {
flex: 1;
@ -284,6 +344,13 @@
}
}
// Save roll card specific
.vagabond.chat-card.save-roll {
.save-name {
margin: 0;
}
}
// Attack roll card specific
.vagabond.chat-card.attack-roll {
.card-header {
@ -295,7 +362,7 @@
width: 32px;
height: 32px;
border-radius: $radius-sm;
border: 1px solid $color-border;
border: 1px solid var(--color-border);
}
.header-text {
@ -304,13 +371,14 @@
.weapon-name {
margin: 0;
font-size: $font-size-base;
color: var(--color-text-primary);
}
.attack-type-badge {
font-size: $font-size-xs;
padding: $spacing-1 $spacing-2;
background-color: rgba($color-accent-primary, 0.2);
color: $color-accent-primary;
background-color: rgba(139, 69, 19, 0.2);
color: var(--color-accent-primary);
border-radius: $radius-full;
font-weight: $font-weight-medium;
}
@ -318,18 +386,18 @@
}
.damage-section {
margin-top: $spacing-3;
margin: $spacing-3;
padding: $spacing-3;
background-color: rgba($color-danger, 0.1);
border: 1px solid rgba($color-danger, 0.3);
background-color: rgba(201, 68, 68, 0.1);
border: 1px solid rgba(201, 68, 68, 0.3);
border-radius: $radius-md;
&.critical {
background-color: rgba($color-warning, 0.15);
border-color: $color-warning;
background-color: rgba(212, 163, 44, 0.15);
border-color: var(--color-warning);
.damage-total {
color: $color-warning;
color: var(--color-warning);
}
}
@ -338,13 +406,14 @@
gap: $spacing-2;
font-weight: $font-weight-semibold;
margin-bottom: $spacing-2;
color: var(--color-text-primary);
i {
color: $color-danger;
color: var(--color-danger);
}
.crit-label {
color: $color-warning;
color: var(--color-warning);
font-size: $font-size-sm;
}
}
@ -357,13 +426,13 @@
font-family: $font-family-header;
font-size: $font-size-3xl;
font-weight: $font-weight-bold;
color: $color-danger;
color: var(--color-danger);
line-height: 1;
}
.damage-type {
font-size: $font-size-sm;
color: $color-text-secondary;
color: var(--color-text-secondary);
text-transform: capitalize;
}
}
@ -374,10 +443,10 @@
margin-top: $spacing-2;
font-family: $font-family-mono;
font-size: $font-size-sm;
color: $color-text-muted;
color: var(--color-text-muted);
.grip-indicator {
color: $color-text-secondary;
color: var(--color-text-secondary);
}
}
}
@ -386,32 +455,54 @@
display: flex;
flex-wrap: wrap;
gap: $spacing-1;
margin-top: $spacing-2;
margin: $spacing-2 $spacing-3;
padding: $spacing-2;
.property-tag {
font-size: $font-size-xs;
padding: $spacing-1 $spacing-2;
background-color: $color-parchment-dark;
background-color: var(--color-bg-secondary);
border-radius: $radius-full;
color: $color-text-secondary;
color: var(--color-text-secondary);
text-transform: capitalize;
}
}
}
// Spell card specific
.vagabond.chat-card.spell-card {
.vagabond.chat-card.spell-card,
.vagabond.chat-card.spell-cast {
.card-header {
.spell-name {
margin: 0;
}
.mana-cost-badge {
font-size: $font-size-xs;
padding: $spacing-1 $spacing-2;
background-color: rgba(112, 80, 176, 0.2);
color: var(--color-reason);
border-radius: $radius-full;
font-weight: $font-weight-medium;
i {
margin-right: $spacing-1;
}
}
}
.spell-effect {
padding: $spacing-3;
font-style: italic;
border-left: 3px solid $color-accent-primary;
background-color: $color-parchment-light;
margin: $spacing-3 0;
border-left: 3px solid var(--color-accent-primary);
background-color: var(--color-bg-secondary);
margin: $spacing-3;
color: var(--color-text-secondary);
}
.spell-meta {
@include grid(2, $spacing-2);
margin: 0 $spacing-3 $spacing-3;
font-size: $font-size-sm;
.meta-item {
@ -419,10 +510,45 @@
padding: $spacing-1;
.meta-label {
color: $color-text-muted;
color: var(--color-text-muted);
}
.meta-value {
font-weight: $font-weight-medium;
color: var(--color-text-primary);
}
}
}
// Mana spent display
.mana-spent {
@include flex-center;
gap: $spacing-2;
margin: 0 $spacing-3 $spacing-3;
padding: $spacing-2;
background-color: rgba(112, 80, 176, 0.1);
border: 1px solid rgba(112, 80, 176, 0.3);
border-radius: $radius-md;
font-size: $font-size-sm;
color: var(--color-reason);
i {
font-size: $font-size-base;
}
}
// Focus indicator
.focus-indicator {
@include flex-center;
gap: $spacing-2;
margin: 0 $spacing-3 $spacing-3;
padding: $spacing-2;
background-color: rgba(212, 163, 44, 0.1);
border: 1px solid var(--color-warning);
border-radius: $radius-md;
font-size: $font-size-sm;
color: var(--color-warning);
}
}
// Animations

View File

@ -33,11 +33,11 @@
padding: 0;
background-color: transparent;
border-color: transparent;
color: $color-text-primary;
color: var(--color-text-primary);
&:hover:not(:disabled) {
background-color: $color-parchment-dark;
color: $color-accent-primary;
background-color: var(--color-bg-secondary);
color: var(--color-accent-primary);
}
i {
@ -52,14 +52,14 @@
width: 24px;
height: 24px;
padding: 0;
background-color: $color-parchment;
border: 1px solid $color-border;
color: $color-text-primary;
background-color: var(--color-bg-primary);
border: 1px solid var(--color-border);
color: var(--color-text-primary);
&:hover:not(:disabled) {
background-color: $color-parchment-dark;
border-color: $color-accent-primary;
color: $color-accent-primary;
background-color: var(--color-bg-secondary);
border-color: var(--color-accent-primary);
color: var(--color-accent-primary);
}
i {
@ -71,13 +71,13 @@
// Rollable button (for dice rolls)
.btn-roll {
@include button-base;
background-color: $color-parchment-light;
color: $color-text-primary;
border-color: $color-border;
background-color: var(--color-bg-input);
color: var(--color-text-primary);
border-color: var(--color-border);
&:hover:not(:disabled) {
background-color: $color-accent-highlight;
border-color: $color-accent-primary;
background-color: var(--color-accent-highlight);
border-color: var(--color-accent-primary);
}
i {
@ -88,24 +88,24 @@
// Danger button
.btn-danger {
@include button-base;
background-color: $color-danger;
color: $color-text-inverse;
border-color: darken($color-danger, 10%);
background-color: var(--color-danger);
color: var(--color-text-inverse);
border-color: var(--color-danger);
&:hover:not(:disabled) {
background-color: darken($color-danger, 10%);
filter: brightness(0.85);
}
}
// Success button
.btn-success {
@include button-base;
background-color: $color-success;
color: $color-text-inverse;
border-color: darken($color-success, 10%);
background-color: var(--color-success);
color: var(--color-text-inverse);
border-color: var(--color-success);
&:hover:not(:disabled) {
background-color: darken($color-success, 10%);
filter: brightness(0.85);
}
}

View File

@ -41,8 +41,8 @@
padding: $spacing-2 $spacing-8 $spacing-2 $spacing-3;
cursor: pointer;
appearance: none;
background-color: $color-parchment-light !important;
color: $color-text-primary !important;
background-color: var(--color-bg-input) !important;
color: var(--color-text-primary) !important;
font-size: $font-size-base;
line-height: 1.5;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%232c2416' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
@ -50,13 +50,18 @@
background-position: right $spacing-3 center;
}
// Dark theme select arrow override (per-sheet, following D&D5e pattern)
&.themed.theme-dark select {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23e8dcc8' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
}
// Checkbox
input[type="checkbox"] {
width: 1rem;
height: 1rem;
margin: 0;
cursor: pointer;
accent-color: $color-accent-primary;
accent-color: var(--color-accent-primary);
@include focus-visible;
}
@ -78,7 +83,7 @@
height: 1rem;
margin: 0;
cursor: pointer;
accent-color: $color-accent-primary;
accent-color: var(--color-accent-primary);
@include focus-visible;
}
@ -91,7 +96,7 @@
display: block;
margin-bottom: $spacing-1;
font-weight: $font-weight-medium;
color: $color-text-secondary;
color: var(--color-text-secondary);
}
&.inline {
@ -134,6 +139,7 @@
text-align: center;
background: transparent;
border: none;
color: inherit;
&:focus {
outline: none;
@ -153,7 +159,7 @@
}
.separator {
color: $color-text-muted;
color: var(--color-text-muted);
}
}
@ -166,12 +172,12 @@
transition: all $transition-fast;
&:hover {
border-color: $color-border-light;
border-color: var(--color-border-light);
}
&:focus {
background: $color-parchment-light;
border-color: $color-accent-primary;
background: var(--color-bg-input);
border-color: var(--color-accent-primary);
outline: none;
}
}
@ -179,9 +185,14 @@
// Readonly display (looks like text, not input)
.readonly-value {
padding: $spacing-2 $spacing-3;
background-color: $color-parchment-dark;
border: 1px solid $color-border-light;
background-color: var(--color-bg-secondary);
border: 1px solid var(--color-border-light);
border-radius: $radius-md;
color: $color-text-secondary;
color: var(--color-text-secondary);
}
}
// Global dark theme select arrow override (when body has theme-dark)
body.theme-dark .vagabond select {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23e8dcc8' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
}

View File

@ -9,8 +9,8 @@
margin: 0;
padding: 0;
list-style: none;
border-bottom: 2px solid $color-border;
background-color: $color-parchment-dark;
border-bottom: 2px solid var(--color-border);
background-color: var(--color-bg-secondary);
.item {
margin: 0;
@ -18,7 +18,7 @@
font-family: $font-family-header;
font-size: $font-size-base;
font-weight: $font-weight-medium;
color: $color-text-secondary;
color: var(--color-text-secondary);
background-color: transparent;
border: none;
border-bottom: 2px solid transparent;
@ -27,14 +27,14 @@
transition: all $transition-fast;
&:hover {
color: $color-text-primary;
background-color: $color-parchment;
color: var(--color-text-primary);
background-color: var(--color-bg-primary);
}
&.active {
color: $color-text-primary;
background-color: $color-parchment;
border-bottom-color: $color-accent-primary;
color: var(--color-text-primary);
background-color: var(--color-bg-primary);
border-bottom-color: var(--color-accent-primary);
}
@include focus-visible;
@ -64,7 +64,7 @@
.sheet-tabs.vertical {
flex-direction: column;
border-bottom: none;
border-right: 2px solid $color-border;
border-right: 2px solid var(--color-border);
.item {
border-bottom: none;
@ -73,7 +73,7 @@
margin-right: -2px;
&.active {
border-right-color: $color-accent-primary;
border-right-color: var(--color-accent-primary);
}
}
}
@ -84,12 +84,12 @@
gap: $spacing-2;
margin-bottom: $spacing-4;
padding-bottom: $spacing-2;
border-bottom: 1px solid $color-border-light;
border-bottom: 1px solid var(--color-border-light);
.sub-tab {
padding: $spacing-1 $spacing-3;
font-size: $font-size-sm;
color: $color-text-muted;
color: var(--color-text-muted);
background-color: transparent;
border: 1px solid transparent;
border-radius: $radius-md;
@ -97,14 +97,14 @@
transition: all $transition-fast;
&:hover {
color: $color-text-primary;
background-color: $color-parchment-dark;
color: var(--color-text-primary);
background-color: var(--color-bg-secondary);
}
&.active {
color: $color-text-primary;
background-color: $color-parchment-dark;
border-color: $color-border;
color: var(--color-text-primary);
background-color: var(--color-bg-secondary);
border-color: var(--color-border);
}
}
}

View File

@ -1,18 +1,21 @@
// Vagabond RPG - Roll Dialog Styles
// ==================================
// All roll dialogs use CSS custom properties for theme support.
// ApplicationV2 Roll Dialog Base
.vagabond.roll-dialog {
// Force light background on the entire window
background-color: $color-parchment;
color: $color-text-primary;
// Use themed class for proper theme support
&.themed {
background-color: var(--color-bg-primary);
color: var(--color-text-primary);
}
.roll-dialog-content {
@include flex-column;
gap: $spacing-3;
padding: $spacing-4;
background-color: $color-parchment;
color: $color-text-primary;
background-color: var(--color-bg-primary);
color: var(--color-text-primary);
}
// Automatic favor/hinder from Active Effects
@ -28,30 +31,60 @@
}
&.favor {
background-color: rgba($color-success, 0.15);
color: $color-success;
border: 1px solid $color-success;
background-color: rgba(74, 159, 66, 0.15);
color: var(--color-success);
border: 1px solid var(--color-success);
}
&.hinder {
background-color: rgba($color-danger, 0.15);
color: $color-danger;
border: 1px solid $color-danger;
background-color: rgba(201, 68, 68, 0.15);
color: var(--color-danger);
border: 1px solid var(--color-danger);
}
}
// Skill selection
.skill-selection {
// Common selection dropdowns
.skill-selection,
.weapon-selection,
.save-selection,
.spell-selection {
@include flex-column;
gap: $spacing-2;
label {
font-weight: $font-weight-semibold;
color: var(--color-text-primary);
}
select {
@include input-base;
width: 100%;
cursor: pointer;
}
.no-weapons-message,
.no-spells-warning {
@include flex-center;
gap: $spacing-2;
padding: $spacing-3;
text-align: center;
color: var(--color-text-muted);
font-style: italic;
background-color: var(--color-bg-secondary);
border-radius: $radius-md;
i {
color: var(--color-warning);
}
}
}
// Skill info panel
.skill-info {
// Info panels (skill-info, attack-info, save-info, spell-info)
.skill-info,
.attack-info,
.save-info,
.spell-info,
.casting-skill {
@include panel;
@include grid(2, $spacing-2);
padding: $spacing-3;
@ -62,28 +95,113 @@
.label {
font-size: $font-size-sm;
color: $color-text-muted;
color: var(--color-text-muted);
}
.value {
font-weight: $font-weight-medium;
color: var(--color-text-primary);
&.trained {
color: $color-success;
color: var(--color-success);
}
&.untrained {
color: $color-text-muted;
color: var(--color-text-muted);
}
&.difficulty {
font-family: $font-family-header;
font-size: $font-size-lg;
font-weight: $font-weight-bold;
color: var(--color-accent-primary);
}
&.crit {
color: $color-warning;
color: var(--color-warning);
}
}
.stat-name {
font-weight: $font-weight-medium;
}
}
// Single-column info (for spell dialog)
.spell-info {
@include flex-column;
gap: $spacing-2;
> div {
@include flex-between;
}
}
// Damage preview panel
.damage-preview {
@include panel;
padding: $spacing-3;
background-color: rgba(201, 68, 68, 0.08);
border-color: rgba(201, 68, 68, 0.3);
.damage-formula {
@include flex-center;
gap: $spacing-2;
.label {
font-size: $font-size-sm;
color: var(--color-text-muted);
}
.value {
font-family: $font-family-mono;
font-weight: $font-weight-bold;
font-size: $font-size-lg;
color: var(--color-danger);
}
.damage-type {
font-size: $font-size-sm;
color: var(--color-text-secondary);
}
}
.weapon-properties {
display: flex;
flex-wrap: wrap;
gap: $spacing-1;
margin-top: $spacing-2;
justify-content: center;
.property-tag {
font-size: $font-size-xs;
padding: $spacing-1 $spacing-2;
background-color: var(--color-bg-secondary);
border-radius: $radius-full;
color: var(--color-text-secondary);
text-transform: uppercase;
}
}
}
// Versatile weapon toggle
.versatile-toggle {
@include flex-center;
padding: $spacing-2;
.checkbox-label {
@include flex-center;
gap: $spacing-2;
cursor: pointer;
input[type="checkbox"] {
width: 1.25rem;
height: 1.25rem;
accent-color: var(--color-accent-primary);
}
span {
color: var(--color-text-primary);
}
}
}
@ -95,6 +213,7 @@
> label {
font-weight: $font-weight-semibold;
color: var(--color-text-primary);
}
.favor-hinder-toggles {
@ -109,18 +228,18 @@
&.favor-btn {
&.active,
&:hover {
background-color: rgba($color-success, 0.15);
border-color: $color-success;
color: $color-success;
background-color: rgba(74, 159, 66, 0.15);
border-color: var(--color-success);
color: var(--color-success);
}
}
&.hinder-btn {
&.active,
&:hover {
background-color: rgba($color-danger, 0.15);
border-color: $color-danger;
color: $color-danger;
background-color: rgba(201, 68, 68, 0.15);
border-color: var(--color-danger);
color: var(--color-danger);
}
}
}
@ -128,19 +247,24 @@
.net-favor-hinder {
@include flex-center;
padding: $spacing-1 $spacing-2;
gap: $spacing-2;
padding: $spacing-2 $spacing-3;
font-size: $font-size-sm;
font-weight: $font-weight-medium;
border-radius: $radius-md;
i {
font-size: $font-size-base;
}
&.favor {
background-color: rgba($color-success, 0.15);
color: $color-success;
background-color: rgba(74, 159, 66, 0.15);
color: var(--color-success);
}
&.hinder {
background-color: rgba($color-danger, 0.15);
color: $color-danger;
background-color: rgba(201, 68, 68, 0.15);
color: var(--color-danger);
}
}
}
@ -152,6 +276,7 @@
> label {
font-weight: $font-weight-semibold;
color: var(--color-text-primary);
}
.modifier-presets {
@ -166,7 +291,7 @@
font-size: $font-size-sm;
&:hover {
background-color: $color-parchment-dark;
background-color: var(--color-bg-secondary);
}
}
}
@ -196,129 +321,310 @@
opacity: 0.5;
cursor: not-allowed;
}
.insufficient-mana-label {
font-size: $font-size-sm;
opacity: 0.8;
}
}
}
}
// Skill check dialog specific
.vagabond.skill-check-dialog {
// Additional skill-specific styles if needed
}
// Attack roll dialog specific
.vagabond.attack-roll-dialog {
.weapon-selection {
// ===========================================
// Save Roll Dialog Specific Styles
// ===========================================
.vagabond.roll-dialog {
// Defense options (Block/Dodge for Reflex saves)
.defense-options {
@include flex-column;
gap: $spacing-2;
margin-top: $spacing-2;
label {
> label {
font-weight: $font-weight-semibold;
color: var(--color-text-primary);
}
.no-weapons-message {
padding: $spacing-3;
text-align: center;
color: $color-text-muted;
font-style: italic;
}
}
.defense-buttons {
display: flex;
gap: $spacing-2;
.attack-info {
@include panel;
@include grid(2, $spacing-2);
padding: $spacing-3;
.defense-btn {
@include button-secondary;
flex: 1;
gap: $spacing-2;
padding: $spacing-3;
> div {
@include flex-between;
i {
font-size: $font-size-lg;
}
&.dodge-btn {
&.active,
&:hover:not(:disabled) {
background-color: rgba(74, 159, 66, 0.15);
border-color: var(--color-success);
color: var(--color-success);
}
}
&.block-btn {
&.active,
&:hover:not(:disabled) {
background-color: rgba(64, 144, 224, 0.15);
border-color: var(--color-info);
color: var(--color-info);
}
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
&:hover {
background-color: transparent;
border-color: var(--color-border);
color: var(--color-text-muted);
}
}
}
}
.label {
.defense-info {
@include flex-center;
gap: $spacing-2;
padding: $spacing-2 $spacing-3;
font-size: $font-size-sm;
color: $color-text-muted;
}
color: var(--color-text-secondary);
background-color: var(--color-bg-secondary);
border-radius: $radius-md;
.value {
font-weight: $font-weight-medium;
&.difficulty {
font-family: $font-family-header;
font-size: $font-size-lg;
font-weight: $font-weight-bold;
}
&.crit {
color: $color-warning;
i {
color: var(--color-info);
}
}
}
}
.damage-preview {
// ===========================================
// Spell Cast Dialog Specific Styles
// ===========================================
.vagabond.roll-dialog {
// Mana display
.mana-display {
@include panel;
@include flex-between;
padding: $spacing-3;
background-color: rgba($color-danger, 0.1);
border-color: rgba($color-danger, 0.3);
background: linear-gradient(135deg, rgba(112, 80, 176, 0.1), rgba(112, 80, 176, 0.05));
border-color: rgba(112, 80, 176, 0.3);
.damage-formula {
.mana-current,
.mana-cost {
@include flex-center;
gap: $spacing-2;
.label {
font-size: $font-size-sm;
color: $color-text-muted;
color: var(--color-text-muted);
}
.value {
font-family: $font-family-header;
font-size: $font-size-xl;
font-weight: $font-weight-bold;
color: var(--color-reason);
&.insufficient {
color: var(--color-danger);
}
}
}
}
// Spell effect section
.spell-effect {
@include panel;
padding: $spacing-3;
background-color: var(--color-bg-secondary);
.effect-header {
margin-bottom: $spacing-2;
.include-effect-toggle {
@include flex-center;
justify-content: flex-start;
gap: $spacing-2;
cursor: pointer;
input[type="checkbox"] {
width: 1.25rem;
height: 1.25rem;
accent-color: var(--color-accent-primary);
}
span {
font-weight: $font-weight-semibold;
color: var(--color-text-primary);
}
}
}
.effect-text {
font-size: $font-size-sm;
color: var(--color-text-secondary);
padding: $spacing-2;
background-color: var(--color-bg-primary);
border-radius: $radius-sm;
border-left: 3px solid var(--color-accent-primary);
}
}
// Damage configuration (dice slider)
.damage-config {
@include flex-column;
gap: $spacing-2;
> label {
font-weight: $font-weight-semibold;
color: var(--color-text-primary);
}
.damage-dice-input {
@include flex-center;
gap: $spacing-3;
input[type="range"] {
flex: 1;
height: 8px;
border-radius: $radius-full;
background: var(--color-bg-tertiary);
outline: none;
cursor: pointer;
&::-webkit-slider-thumb {
-webkit-appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: var(--color-danger);
cursor: pointer;
border: 2px solid var(--color-bg-primary);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
&::-moz-range-thumb {
width: 20px;
height: 20px;
border-radius: 50%;
background: var(--color-danger);
cursor: pointer;
border: 2px solid var(--color-bg-primary);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
}
.dice-count {
font-family: $font-family-mono;
font-size: $font-size-lg;
font-weight: $font-weight-bold;
color: var(--color-danger);
min-width: 4rem;
text-align: center;
}
}
.damage-preview {
@include flex-center;
gap: $spacing-2;
padding: $spacing-2;
background-color: rgba(201, 68, 68, 0.08);
border-radius: $radius-md;
.label {
font-size: $font-size-sm;
color: var(--color-text-muted);
}
.value {
font-family: $font-family-mono;
font-weight: $font-weight-bold;
font-size: $font-size-lg;
color: var(--color-danger);
}
.damage-type {
font-size: $font-size-sm;
color: $color-text-secondary;
}
}
.weapon-properties {
display: flex;
flex-wrap: wrap;
gap: $spacing-1;
margin-top: $spacing-2;
justify-content: center;
.property-tag {
font-size: $font-size-xs;
padding: $spacing-1 $spacing-2;
background-color: $color-parchment-dark;
border-radius: $radius-full;
color: $color-text-secondary;
text-transform: uppercase;
color: var(--color-text-secondary);
}
}
}
.versatile-toggle {
@include flex-center;
padding: $spacing-2;
// Delivery and Duration selection
.delivery-selection,
.duration-selection {
@include flex-column;
gap: $spacing-2;
.checkbox-label {
> label {
font-weight: $font-weight-semibold;
color: var(--color-text-primary);
}
select {
@include input-base;
width: 100%;
cursor: pointer;
}
}
// Focus warning
.focus-warning {
@include panel;
@include flex-column;
gap: $spacing-2;
padding: $spacing-3;
background-color: rgba(212, 163, 44, 0.1);
border-color: var(--color-warning);
> i {
color: var(--color-warning);
}
> span {
font-size: $font-size-sm;
color: var(--color-text-secondary);
}
.current-focus {
font-size: $font-size-sm;
color: var(--color-text-muted);
font-style: italic;
}
.focus-limit-warning {
@include flex-center;
gap: $spacing-2;
cursor: pointer;
padding: $spacing-2;
font-size: $font-size-sm;
font-weight: $font-weight-medium;
color: var(--color-danger);
background-color: rgba(201, 68, 68, 0.1);
border-radius: $radius-md;
input[type="checkbox"] {
width: 1.25rem;
height: 1.25rem;
accent-color: $color-accent-primary;
i {
color: var(--color-danger);
}
}
}
}
// ===========================================
// Legacy dialog styles (for backward compatibility)
// ===========================================
.vagabond.dialog.roll-dialog {
.dialog-content {
padding: $spacing-4;
background-color: var(--color-bg-primary);
color: var(--color-text-primary);
}
// Roll info section
@ -335,14 +641,15 @@
font-family: $font-family-header;
font-size: $font-size-3xl;
font-weight: $font-weight-bold;
color: var(--color-accent-primary);
}
.crit-threshold {
font-size: $font-size-sm;
color: $color-text-muted;
color: var(--color-text-muted);
.crit-value {
color: $color-accent-primary;
color: var(--color-warning);
font-weight: $font-weight-bold;
}
}
@ -361,14 +668,15 @@
label {
font-weight: $font-weight-medium;
color: var(--color-text-primary);
}
&.favor .toggle-active {
color: $color-success;
color: var(--color-success);
}
&.hinder .toggle-active {
color: $color-danger;
color: var(--color-danger);
}
}
}
@ -398,7 +706,7 @@
}
}
// Spell cast dialog specific
// Legacy spell cast dialog
.vagabond.dialog.spell-cast-dialog {
.mana-cost {
@include panel;
@ -408,20 +716,21 @@
.cost-label {
font-weight: $font-weight-semibold;
color: var(--color-text-primary);
}
.cost-value {
font-family: $font-family-header;
font-size: $font-size-xl;
font-weight: $font-weight-bold;
color: $color-accent-primary;
color: var(--color-reason);
}
&.insufficient {
border-color: $color-danger;
border-color: var(--color-danger);
.cost-value {
color: $color-danger;
color: var(--color-danger);
}
}
}
@ -434,6 +743,7 @@
.selection-label {
font-weight: $font-weight-semibold;
margin-bottom: $spacing-2;
color: var(--color-text-primary);
}
.selection-grid {
@ -447,35 +757,34 @@
transition: all $transition-fast;
&:hover {
border-color: $color-accent-primary;
border-color: var(--color-accent-primary);
}
&.selected {
background-color: $color-parchment-dark;
border-color: $color-accent-primary;
background-color: var(--color-bg-secondary);
border-color: var(--color-accent-primary);
}
.option-name {
font-weight: $font-weight-medium;
color: var(--color-text-primary);
}
.option-cost {
font-size: $font-size-sm;
color: $color-text-muted;
color: var(--color-text-muted);
}
}
}
}
// ===========================================
// Favor/Hinder Debug Panel
// ===========================================
.vagabond.favor-hinder-debug {
// Force light background on the entire window content
// Using !important to override Foundry's dark theme styles
background-color: $color-parchment !important;
color: $color-text-primary !important;
* {
color: $color-text-primary;
&.themed {
background-color: var(--color-bg-primary);
color: var(--color-text-primary);
}
// Enable scrolling on the window content
@ -489,7 +798,7 @@
@include flex-column;
gap: $spacing-4;
padding: $spacing-4;
background-color: $color-parchment !important;
background-color: var(--color-bg-primary);
}
.actor-selection {
@ -498,7 +807,7 @@
label {
font-weight: $font-weight-semibold;
color: $color-text-primary;
color: var(--color-text-primary);
}
}
@ -510,7 +819,7 @@
.debug-panel {
@include panel;
padding: $spacing-3;
background-color: $color-parchment-light;
background-color: var(--color-bg-secondary);
h3 {
@include flex-center;
@ -518,12 +827,12 @@
gap: $spacing-2;
margin: 0 0 $spacing-3 0;
padding-bottom: $spacing-2;
border-bottom: 1px solid $color-border;
border-bottom: 1px solid var(--color-border);
font-size: $font-size-base;
color: $color-text-primary;
color: var(--color-text-primary);
i {
color: $color-accent-primary;
color: var(--color-accent-primary);
}
}
}
@ -531,21 +840,21 @@
.flag-table {
width: 100%;
border-collapse: collapse;
background-color: $color-parchment-light;
background-color: var(--color-bg-secondary);
th,
td {
padding: $spacing-2;
text-align: left;
border-bottom: 1px solid $color-border-light;
color: $color-text-primary;
border-bottom: 1px solid var(--color-border-light);
color: var(--color-text-primary);
}
th {
font-size: $font-size-sm;
font-weight: $font-weight-semibold;
color: $color-text-secondary;
background-color: $color-parchment-dark;
color: var(--color-text-secondary);
background-color: var(--color-bg-tertiary);
&.center {
text-align: center;
@ -553,7 +862,7 @@
}
td {
background-color: $color-parchment-light !important;
background-color: var(--color-bg-secondary);
&.center {
text-align: center;
@ -564,17 +873,16 @@
}
}
// Alternating row colors with good contrast
tbody tr:nth-child(even) td {
background-color: $color-parchment !important;
background-color: var(--color-bg-primary);
}
.stat-tag {
font-size: $font-size-xs;
padding: $spacing-1 $spacing-2;
background-color: $color-parchment-dark;
background-color: var(--color-bg-tertiary);
border-radius: $radius-full;
color: $color-text-secondary;
color: var(--color-text-secondary);
text-transform: uppercase;
}
@ -582,11 +890,11 @@
width: 1.25rem;
height: 1.25rem;
cursor: pointer;
accent-color: $color-accent-primary;
accent-color: var(--color-accent-primary);
}
tbody tr:hover td {
background-color: $color-parchment-dark;
background-color: var(--color-bg-tertiary);
}
}
@ -594,7 +902,7 @@
display: flex;
gap: $spacing-3;
padding-top: $spacing-3;
border-top: 1px solid $color-border;
border-top: 1px solid var(--color-border);
button {
@include button-base;
@ -608,11 +916,11 @@
.clear-btn {
@include button-secondary;
color: $color-danger;
border-color: $color-danger;
color: var(--color-danger);
border-color: var(--color-danger);
&:hover:not(:disabled) {
background-color: rgba($color-danger, 0.1);
background-color: rgba(201, 68, 68, 0.1);
}
}
}
@ -623,7 +931,7 @@
gap: $spacing-3;
padding: $spacing-6;
text-align: center;
color: $color-text-muted;
color: var(--color-text-muted);
i {
font-size: $font-size-4xl;

File diff suppressed because it is too large Load Diff

View File

@ -4,25 +4,36 @@
.vagabond.sheet.item {
min-width: 500px;
min-height: 400px;
display: flex;
flex-direction: column;
// Utility class for hiding inactive tabs
.hidden {
display: none !important;
}
// Header styling
.sheet-header.item-header {
@include flex-between;
padding: $spacing-4;
background-color: $color-parchment-dark;
border-bottom: 2px solid $color-border;
background-color: var(--color-bg-secondary);
border-bottom: 2px solid var(--color-border);
gap: $spacing-4;
.item-img {
width: 64px;
height: 64px;
object-fit: cover;
border: 2px solid $color-border;
border: 2px solid var(--color-border);
border-radius: $radius-md;
cursor: pointer;
transition: all $transition-fast;
box-shadow: 0 1px 3px var(--shadow-light);
&:hover {
border-color: $color-accent-primary;
border-color: var(--color-accent-primary);
box-shadow: 0 2px 6px var(--shadow-medium);
transform: scale(1.02);
}
}
@ -37,14 +48,23 @@
input {
width: 100%;
font-size: $font-size-lg;
font-weight: bold;
font-family: $font-family-header;
font-size: $font-size-xl;
font-weight: $font-weight-bold;
color: var(--color-text-primary);
border: none;
background: transparent;
border-bottom: 1px solid transparent;
padding: $spacing-1 0;
transition: border-color $transition-fast;
&:hover {
border-bottom-color: var(--color-border-light);
}
&:focus {
border-bottom-color: $color-accent-primary;
border-bottom-color: var(--color-accent-primary);
outline: none;
}
}
}
@ -56,35 +76,64 @@
font-size: $font-size-sm;
font-weight: 500;
text-transform: capitalize;
background-color: $color-parchment;
border: 1px solid $color-border;
background-color: var(--color-bg-primary);
border: 1px solid var(--color-border);
border-radius: $radius-full;
&.weapon {
background-color: mix($color-danger, $color-parchment, 15%);
background-color: var(--color-badge-weapon);
}
&.armor {
background-color: mix($color-info, $color-parchment, 15%);
background-color: var(--color-badge-armor);
}
&.spell {
background-color: mix($color-reason, $color-parchment, 15%);
background-color: var(--color-badge-spell);
}
&.perk {
background-color: mix($color-success, $color-parchment, 15%);
background-color: var(--color-badge-perk);
}
&.class {
background-color: mix($color-warning, $color-parchment, 15%);
background-color: var(--color-badge-class);
}
&.feature {
background-color: var(--color-badge-feature);
}
&.ancestry {
background-color: var(--color-badge-ancestry);
}
&.equipment {
background-color: var(--color-badge-equipment);
}
}
}
}
}
// Sheet body
.sheet-body {
// Tab content areas (both .sheet-body and .tab-content)
.sheet-body,
.tab-content {
padding: $spacing-4;
overflow-y: auto;
max-height: calc(100% - 80px);
background-color: var(--color-bg-primary);
@include custom-scrollbar;
// When used as standalone body (no tabs)
&.sheet-body {
flex: 1;
min-height: 0; // Important for flexbox scrolling
}
}
// Details tab specific
.details-tab {
&.sheet-body {
// Combined class - body that's also the details tab
}
}
// Effects tab has its own padding defined in .effects-tab
.effects-tab.tab-content {
padding: 0; // Remove duplicate padding, .effects-tab handles it
}
// Item content container
@ -120,9 +169,11 @@
}
label {
font-size: $font-size-sm;
font-weight: 500;
color: $color-text-secondary;
font-size: $font-size-xs;
font-weight: $font-weight-semibold;
color: var(--color-text-secondary);
text-transform: uppercase;
letter-spacing: 0.03em;
}
input[type="text"],
@ -130,17 +181,29 @@
select,
textarea {
padding: $spacing-2;
border: 1px solid $color-border;
border: 1px solid var(--color-border);
border-radius: $radius-sm;
background-color: $color-parchment-light;
background-color: var(--color-bg-input);
font-family: $font-family-body;
font-size: $font-size-sm;
color: var(--color-text-primary);
transition:
border-color $transition-fast,
box-shadow $transition-fast;
&:hover:not(:disabled) {
border-color: var(--color-border-dark);
}
&:focus {
border-color: $color-accent-primary;
border-color: var(--color-accent-primary);
box-shadow: 0 0 0 2px rgba(139, 69, 19, 0.15);
outline: none;
}
&:disabled {
background-color: $color-parchment;
background-color: var(--color-bg-primary);
color: var(--color-text-muted);
cursor: not-allowed;
}
}
@ -150,6 +213,12 @@
height: 18px;
margin: 0;
cursor: pointer;
accent-color: var(--color-accent-primary);
&:disabled {
cursor: not-allowed;
opacity: 0.6;
}
}
input[type="number"] {
@ -168,7 +237,7 @@
.units {
font-size: $font-size-sm;
color: $color-text-secondary;
color: var(--color-text-secondary);
}
textarea {
@ -180,11 +249,11 @@
// Fieldset styling
fieldset {
border: 1px solid $color-border;
border: 1px solid var(--color-border);
border-radius: $radius-md;
padding: $spacing-3;
margin: 0;
background-color: rgba($color-parchment-dark, 0.3);
background-color: var(--color-bg-highlight);
legend {
padding: 0 $spacing-2;
@ -254,15 +323,55 @@
font-weight: 600;
}
.editor-container {
border: 1px solid $color-border;
.editor-wrapper {
border: 1px solid var(--color-border);
border-radius: $radius-sm;
overflow: hidden;
background-color: var(--color-bg-input);
min-height: 120px;
max-height: 400px;
overflow: auto;
resize: vertical;
.editor {
min-height: 120px;
// Static content display (non-editable)
> .editor-content {
min-height: 100px;
padding: $spacing-2;
background-color: $color-parchment-light;
}
// ProseMirror custom element styling
prose-mirror {
display: flex;
flex-direction: column;
min-height: 100px;
// The menu bar that appears when editing
> menu.editor-menu {
flex: 0 0 auto;
position: sticky;
top: 0;
z-index: 1;
background-color: var(--color-bg-secondary);
border-bottom: 1px solid var(--color-border);
}
// Foundry's inner editor-container
> .editor-container {
flex: 1 1 auto;
display: flex;
flex-direction: column;
min-height: 80px;
// The actual editable content area
> .editor-content {
flex: 1 1 auto;
padding: $spacing-2;
}
}
// Toggle button
> button.toggle {
display: none; // Hide since we use toggled="false"
}
}
}
}
@ -275,9 +384,9 @@
.trait-entry {
padding: $spacing-3;
border: 1px solid $color-border;
border: 1px solid var(--color-border);
border-radius: $radius-sm;
background-color: $color-parchment-light;
background-color: var(--color-bg-input);
.trait-header {
display: flex;
@ -291,7 +400,7 @@
button {
@include button-icon;
color: $color-danger;
color: var(--color-danger);
}
}
@ -302,7 +411,7 @@
}
.no-traits {
color: $color-text-secondary;
color: var(--color-text-secondary);
font-style: italic;
text-align: center;
padding: $spacing-4;
@ -330,7 +439,7 @@
label {
font-size: $font-size-xs;
font-weight: 600;
color: $color-text-secondary;
color: var(--color-text-secondary);
}
input {
@ -364,7 +473,7 @@
.hint {
font-size: $font-size-xs;
color: $color-text-secondary;
color: var(--color-text-secondary);
font-style: italic;
}
}
@ -380,7 +489,7 @@
grid-template-columns: 50px 60px 80px 80px 1fr 40px;
gap: $spacing-2;
padding: $spacing-2;
background-color: $color-parchment-dark;
background-color: var(--color-bg-secondary);
font-size: $font-size-xs;
font-weight: 600;
border-radius: $radius-sm $radius-sm 0 0;
@ -391,8 +500,8 @@
grid-template-columns: 50px 60px 80px 80px 1fr 40px;
gap: $spacing-2;
padding: $spacing-2;
border-bottom: 1px solid $color-border;
background-color: $color-parchment-light;
border-bottom: 1px solid var(--color-border);
background-color: var(--color-bg-input);
&:last-child {
border-radius: 0 0 $radius-sm $radius-sm;
@ -406,14 +515,14 @@
button {
@include button-icon;
color: $color-danger;
color: var(--color-danger);
}
}
.no-progression {
padding: $spacing-4;
text-align: center;
color: $color-text-secondary;
color: var(--color-text-secondary);
font-style: italic;
}
}
@ -429,9 +538,9 @@
.feature-entry {
padding: $spacing-3;
border: 1px solid $color-border;
border: 1px solid var(--color-border);
border-radius: $radius-sm;
background-color: $color-parchment-light;
background-color: var(--color-bg-input);
.feature-header {
display: flex;
@ -458,7 +567,7 @@
button {
@include button-icon;
color: $color-danger;
color: var(--color-danger);
}
}
@ -471,7 +580,7 @@
.no-features {
padding: $spacing-4;
text-align: center;
color: $color-text-secondary;
color: var(--color-text-secondary);
font-style: italic;
}
}
@ -488,14 +597,55 @@
font-weight: 600;
}
.editor-container {
border: 1px solid $color-border;
.editor-wrapper {
border: 1px solid var(--color-border);
border-radius: $radius-sm;
background-color: var(--color-bg-input);
min-height: 80px;
max-height: 300px;
overflow: auto;
resize: vertical;
.editor {
min-height: 80px;
// Static content display (non-editable)
> .editor-content {
min-height: 60px;
padding: $spacing-2;
background-color: $color-parchment-light;
}
// ProseMirror custom element styling
prose-mirror {
display: flex;
flex-direction: column;
min-height: 60px;
// The menu bar that appears when editing
> menu.editor-menu {
flex: 0 0 auto;
position: sticky;
top: 0;
z-index: 1;
background-color: var(--color-bg-secondary);
border-bottom: 1px solid var(--color-border);
}
// Foundry's inner editor-container
> .editor-container {
flex: 1 1 auto;
display: flex;
flex-direction: column;
min-height: 40px;
// The actual editable content area
> .editor-content {
flex: 1 1 auto;
padding: $spacing-2;
}
}
// Toggle button
> button.toggle {
display: none;
}
}
}
}
@ -515,6 +665,94 @@
}
}
// Requirements section (feature)
.requirements-section {
display: flex;
flex-direction: column;
gap: $spacing-2;
> label {
font-size: $font-size-xs;
font-weight: $font-weight-semibold;
color: var(--color-text-secondary);
text-transform: uppercase;
letter-spacing: 0.03em;
}
input {
padding: $spacing-2;
border: 1px solid var(--color-border);
border-radius: $radius-sm;
background-color: var(--color-bg-input);
font-family: $font-family-body;
font-size: $font-size-sm;
color: var(--color-text-primary);
transition:
border-color $transition-fast,
box-shadow $transition-fast;
&:hover:not(:disabled) {
border-color: var(--color-border-dark);
}
&:focus {
border-color: var(--color-accent-primary);
box-shadow: 0 0 0 2px rgba(139, 69, 19, 0.15);
outline: none;
}
&:disabled {
background-color: var(--color-bg-primary);
color: var(--color-text-muted);
cursor: not-allowed;
}
}
}
// Ritual components (perk)
.ritual-components {
display: flex;
flex-direction: column;
gap: $spacing-2;
> label {
font-size: $font-size-xs;
font-weight: $font-weight-semibold;
color: var(--color-text-secondary);
text-transform: uppercase;
letter-spacing: 0.03em;
}
input {
padding: $spacing-2;
border: 1px solid var(--color-border);
border-radius: $radius-sm;
background-color: var(--color-bg-input);
font-family: $font-family-body;
font-size: $font-size-sm;
color: var(--color-text-primary);
transition:
border-color $transition-fast,
box-shadow $transition-fast;
&:hover:not(:disabled) {
border-color: var(--color-border-dark);
}
&:focus {
border-color: var(--color-accent-primary);
box-shadow: 0 0 0 2px rgba(139, 69, 19, 0.15);
outline: none;
}
&:disabled {
background-color: var(--color-bg-primary);
color: var(--color-text-muted);
cursor: not-allowed;
}
}
}
// Effects tab
.effects-tab {
padding: $spacing-4;
@ -561,8 +799,8 @@
align-items: center;
gap: $spacing-2;
padding: $spacing-2;
background-color: $color-parchment-light;
border: 1px solid $color-border;
background-color: var(--color-bg-input);
border: 1px solid var(--color-border);
border-radius: $radius-sm;
&.disabled {
@ -587,7 +825,7 @@
width: 24px;
height: 24px;
border-radius: $radius-sm;
border: 1px solid $color-border;
border: 1px solid var(--color-border);
}
.effect-name {
@ -603,10 +841,10 @@
@include button-icon;
&.delete {
color: $color-danger;
color: var(--color-danger);
&:hover {
background-color: rgba($color-danger, 0.1);
background-color: rgba(201, 68, 68, 0.1);
}
}
}
@ -614,7 +852,7 @@
}
.no-effects {
color: $color-text-secondary;
color: var(--color-text-secondary);
font-style: italic;
text-align: center;
padding: $spacing-4;
@ -622,7 +860,7 @@
.effects-hint {
font-size: $font-size-sm;
color: $color-text-secondary;
color: var(--color-text-secondary);
margin-top: $spacing-2;
i {

View File

@ -5,6 +5,9 @@
@import "variables";
@import "mixins";
// Theme system (CSS custom properties)
@import "theme-variables";
// Base styles
@import "base";

View File

@ -16,6 +16,17 @@
],
"esmodules": ["module/vagabond.mjs"],
"styles": ["styles/vagabond.css"],
"themes": [
{
"name": "light",
"label": "VAGABOND.ThemeLight",
"default": true
},
{
"name": "dark",
"label": "VAGABOND.ThemeDark"
}
],
"languages": [
{
"lang": "en",

View File

@ -64,8 +64,12 @@
{{!-- Description --}}
<div class="item-description">
<label>{{localize "VAGABOND.Description"}}</label>
<div class="editor-container">
{{editor system.description target="system.description" button=true editable=editable engine="prosemirror"}}
<div class="editor-wrapper">
{{#if editable}}
<prose-mirror name="system.description" toggled="false">{{{enrichedDescription}}}</prose-mirror>
{{else}}
<div class="editor-content">{{{enrichedDescription}}}</div>
{{/if}}
</div>
</div>
</div>

View File

@ -128,8 +128,12 @@
{{!-- Description --}}
<div class="item-description">
<label>{{localize "VAGABOND.Description"}}</label>
<div class="editor-container">
{{editor system.description target="system.description" button=true editable=editable engine="prosemirror"}}
<div class="editor-wrapper">
{{#if editable}}
<prose-mirror name="system.description" toggled="false">{{{enrichedDescription}}}</prose-mirror>
{{else}}
<div class="editor-content">{{{enrichedDescription}}}</div>
{{/if}}
</div>
</div>
</div>

View File

@ -56,8 +56,12 @@
{{!-- Starting Pack --}}
<div class="starting-pack-section">
<label>{{localize "VAGABOND.StartingPack"}}</label>
<div class="editor-container">
{{editor system.startingPack target="system.startingPack" button=true editable=editable engine="prosemirror"}}
<div class="editor-wrapper">
{{#if editable}}
<prose-mirror name="system.startingPack" toggled="false">{{{enrichedStartingPack}}}</prose-mirror>
{{else}}
<div class="editor-content">{{{enrichedStartingPack}}}</div>
{{/if}}
</div>
</div>
@ -156,8 +160,12 @@
{{!-- Description --}}
<div class="item-description">
<label>{{localize "VAGABOND.Description"}}</label>
<div class="editor-container">
{{editor system.description target="system.description" button=true editable=editable engine="prosemirror"}}
<div class="editor-wrapper">
{{#if editable}}
<prose-mirror name="system.description" toggled="false">{{{enrichedDescription}}}</prose-mirror>
{{else}}
<div class="editor-content">{{{enrichedDescription}}}</div>
{{/if}}
</div>
</div>
</div>

View File

@ -162,8 +162,12 @@
{{!-- Description --}}
<div class="item-description">
<label>{{localize "VAGABOND.Description"}}</label>
<div class="editor-container">
{{editor system.description target="system.description" button=true editable=editable engine="prosemirror"}}
<div class="editor-wrapper">
{{#if editable}}
<prose-mirror name="system.description" toggled="false">{{{enrichedDescription}}}</prose-mirror>
{{else}}
<div class="editor-content">{{{enrichedDescription}}}</div>
{{/if}}
</div>
</div>
</div>

View File

@ -76,8 +76,12 @@
{{!-- Description --}}
<div class="item-description">
<label>{{localize "VAGABOND.Description"}}</label>
<div class="editor-container">
{{editor system.description target="system.description" button=true editable=editable engine="prosemirror"}}
<div class="editor-wrapper">
{{#if editable}}
<prose-mirror name="system.description" toggled="false">{{{enrichedDescription}}}</prose-mirror>
{{else}}
<div class="editor-content">{{{enrichedDescription}}}</div>
{{/if}}
</div>
</div>
</div>

View File

@ -133,8 +133,12 @@
{{!-- Description --}}
<div class="item-description">
<label>{{localize "VAGABOND.Description"}}</label>
<div class="editor-container">
{{editor system.description target="system.description" button=true editable=editable engine="prosemirror"}}
<div class="editor-wrapper">
{{#if editable}}
<prose-mirror name="system.description" toggled="false">{{{enrichedDescription}}}</prose-mirror>
{{else}}
<div class="editor-content">{{{enrichedDescription}}}</div>
{{/if}}
</div>
</div>
</div>

View File

@ -111,24 +111,36 @@
{{!-- Effect --}}
<div class="spell-effect">
<label>{{localize "VAGABOND.Effect"}}</label>
<div class="editor-container">
{{editor system.effect target="system.effect" button=true editable=editable engine="prosemirror"}}
<div class="editor-wrapper">
{{#if editable}}
<prose-mirror name="system.effect" toggled="false">{{{enrichedEffect}}}</prose-mirror>
{{else}}
<div class="editor-content">{{{enrichedEffect}}}</div>
{{/if}}
</div>
</div>
{{!-- Crit Effect --}}
<div class="spell-crit-effect">
<label>{{localize "VAGABOND.CritEffect"}}</label>
<div class="editor-container">
{{editor system.critEffect target="system.critEffect" button=true editable=editable engine="prosemirror"}}
<div class="editor-wrapper">
{{#if editable}}
<prose-mirror name="system.critEffect" toggled="false">{{{enrichedCritEffect}}}</prose-mirror>
{{else}}
<div class="editor-content">{{{enrichedCritEffect}}}</div>
{{/if}}
</div>
</div>
{{!-- Description --}}
<div class="item-description">
<label>{{localize "VAGABOND.Description"}}</label>
<div class="editor-container">
{{editor system.description target="system.description" button=true editable=editable engine="prosemirror"}}
<div class="editor-wrapper">
{{#if editable}}
<prose-mirror name="system.description" toggled="false">{{{enrichedDescription}}}</prose-mirror>
{{else}}
<div class="editor-content">{{{enrichedDescription}}}</div>
{{/if}}
</div>
</div>
</div>

View File

@ -228,8 +228,12 @@
{{!-- Description --}}
<div class="item-description">
<label>{{localize "VAGABOND.Description"}}</label>
<div class="editor-container">
{{editor system.description target="system.description" button=true editable=editable engine="prosemirror"}}
<div class="editor-wrapper">
{{#if editable}}
<prose-mirror name="system.description" toggled="false">{{{enrichedDescription}}}</prose-mirror>
{{else}}
<div class="editor-content">{{{enrichedDescription}}}</div>
{{/if}}
</div>
</div>
</div>