Skip to content

Theme Polish & UI Fixes — 2026-03-28

Comprehensive changelog for all uncommitted changes. Covers theme tokenization, visual polish, accessibility fixes, and animation preset persistence.

Token System & Theming (theme.css)

New Tokens

  • --accent-revert / --accent-revert-subtle — added to all 5 themes. Used for template/preset "modified" indicators and revert actions. Distinct from --accent-warning (unsaved changes) in both meaning and color.
    • Dark themes get dedicated values (not aliased to --branding-purple) for better contrast:
      • Dark: #C084FC / #2d2440
      • Nord: #B48EAD / #3a3050
      • Mocha: #DDB6FE / #302548
    • Light/Cavai: var(--branding-purple) / var(--brand-purple-bg)
  • --code-editor-bg — softened for light themes: oklch(30% 0.015 270) (light), oklch(30% 0.015 310) (cavai). Was too dark against light surfaces.

Token Adjustments

  • Branding color scale tweaks across themes for better distinctiveness
  • Code editor syntax highlighting updated in both CodeMirror (theme-cavai-html.css) and Ace (theme-cavai-theme.js) themes

Accessibility & Contrast Fixes

Critical: Invisible Text

  • RadioButton.vue — label color was var(--border-subtle) → changed to var(--text-secondary). This caused near-invisible workspace dropdown text in light/cavai themes.
  • AddBlockTool / BlockButton — icon color used hardcoded colors.grey85 from legacy constants → removed, now inherits var(--text-primary) via CSS. Icons were invisible on light themes.

Shortcuts Dialog (ShortcutsDialog.vue, ShortcutCategory.vue, OsTypeToggle.vue)

  • Card background: var(--surface-raised) !important
  • Title: var(--text-primary)
  • Close button: var(--text-muted), hover → var(--text-primary)
  • <kbd> elements: bg --surface-sunken, border --border-default, text --text-secondary
  • Table rows: transparent bg, --surface-hover on hover
  • Description text: --text-primary (was --text-secondary)
  • OsTypeToggle: Aggressive Vuetify deep overrides to eliminate white backgrounds on Mac/Windows toggle buttons. Required overriding .v-btn-toggle, .v-btn, .v-btn::before, and .v-icon at multiple levels with !important.

Visual Polish

AddBlockTool (AddBlockTool.vue, BlockButton.vue)

  • Container shifted 2px left (left: calc(#{$size-30} - 2px)) to cover the "Add block" button behind it
  • Container bg: --surface-raised, border: --border-default
  • Block buttons: bg --surface-active, separator borders between items
  • Label: --surface-raised bg with --text-primary text
  • Removed legacy colors mixin and hardcoded color references

Code Editor (CodeMirrorEditor.vue)

  • Horizontal alignment: margin: $size-8 0 (was $size-8 all sides — misaligned with content above)
  • Added resize grip indicator: 3 diagonal stripes using --border-strong via CSS gradient, positioned bottom-right
  • Border: 1px solid var(--border-subtle), rounded with $border-radius-kilo

Template Revert (TemplateButton.vue)

  • Modified dot: repositioned from right to left (left: -1px; top: -1px), shrunk from 7×7 to 5×5px
  • Dot color: var(--accent-revert) (was --accent-warning)
  • Revert button: bg --accent-revert-subtle, text --accent-revert, border removed, rounded top corners ($border-radius-milli)
  • Triangle arrow pseudo-element: matches --accent-revert-subtle

Animation Preset Revert (AnimationEffectList.vue)

  • Unified with template revert styling:
    • Dot: left: -1px; top: -1px; 5×5px (was left: 3px; top: 3px; 3×3px)
    • Dot color: var(--accent-revert) (was --accent-warning)
    • Revert button: --accent-revert-subtle bg / --accent-revert text (was --surface-raised / --text-secondary)
    • Border removed, rounded top with $border-radius-milli
    • Triangle arrow updated to --accent-revert-subtle

Configuration Panel (ConfigurationPanel.vue)

  • Removed "Select a block to edit" empty state text (misleading — a base block is always default-selected, text was just a loading flash)
  • Kept Bubbles mascot peek with [data-theme="cavai"] scoping

HtmlSection

  • Added ::v-deep .v-btn .v-icon { color: var(--text-secondary) !important; } for info tooltip icon consistency

Flow Editor

Branded Operator Borders (OperatorBase.vue)

  • Changed branded operator outline from solid to dashed: outline: 2px dashed ${resolvedColor}
  • First/starting operator retains solid outline via CSS class

Bug Fix: Animation Preset Revert Persistence

The revert preset button in animations didn't work after navigating away and back — the selected preset name was lost on component destroy.

Root cause: AnimationConfigCard didn't pass lastPreset prop to AnimationEffectList or listen for update:lastPreset. The value only lived in component local data().

Fix (4 files):

  1. types.ts — added lastPreset?: string to AnimationConfig type
  2. defaults.ts — added lastPreset: '' to animationDefaults()
  3. AnimationConfigCard.vue — wired :last-preset and @update:lastPreset through to AnimationEffectList
  4. configurationLogic.ts — added 'update:lastPreset' listener that persists to animation.lastPreset in block data

Broader Token Migration (from prior sessions, included in diff)

~71 files touched total. Key patterns applied across the codebase:

  • Replaced all hardcoded hex colors with semantic tokens
  • Replaced colors.grey* / colors.* legacy constant usage with CSS custom properties
  • Added Vuetify deep overrides (::v-deep with !important) where framework inlines colors
  • Ensured all 5 themes render correctly (dark, light, cavai, nord, mocha)

Files touched (grouped by area)

Common components: Card, CardSection, ColorPicker, Dropdown, InputField, InputSelect, RadioButton, TextArea, ToggleSwitch, TooltipWrapper

Shortcuts: OsTypeToggle, ShortcutCategory, ShortcutsDialog

Creative Wizard: CreativeTypeIcon, SizePreset, TemplateRow

Builder Visuals — Blocks: AddBlockTool, BlockActionsMenu, BlockButton, BlockPanel

Builder Visuals — Configuration: ConfigurationPanel, AnimationConfigCard, AnimationEffectList, AnimationEffectRow, BoxShadowSection, CodeMirrorEditor, CustomCSSEditor, DelaySection, DurationSection, FilterSection, FontSizeSection, HtmlSection, IterationsSection, MarginSection, MultiPostfixInput, OverrideIndicator, PlaybackSection, PositionSection, RotationSection, SlideSection, SlideSectionsPagination, TabNavigation, TemplateButton, CustomBorderRadius

Builder Visuals — Configs: HtmlConfiguration, SliderConfiguration

Builder Visuals — Other: FormatSelector, PreviewFormatSelector, PreviewPanel

Flow editor: NodeLabel, brandingToolbarConstants, OperatorBase

Option rows: OptionRow, OptionInputField, OverrideControls

Builder Delivery: ImpressionPixelOptions, TagCode, VastOptions, VpaidOptions

Global styles: _components-builder.scss, _vuetify-reset.scss, theme.css

Code editors: theme-cavai-html.css, theme-cavai-theme.js

Mixins/logic: configurationLogic.ts

Types/defaults: types.ts, defaults.ts

Tokenization Rule

All component colors MUST use semantic tokens from theme.css. Never hardcode hex values or use legacy colors.* constants for themed UI. The only exception is primitives used in :root definitions.

Internal documentation