Appearance
Cavai Theme Branding Enhancement Implementation Plan
For agentic workers: REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Enhance the Cavai theme with mascot (Bubbles), star decorations, gradient accents, and micro-animations to give it brand personality.
Architecture: CSS-only for decorative elements (stars, gradients, twinkle animations) via _cavai-decorations.scss, scoped under [data-theme="cavai"]. One CavaiMascot.vue component with mode prop for interactive mascot behaviors. All tokens in theme.css. Uses existing useTheme composable for reactive theme detection.
Tech Stack: Vue 2 Options API, SCSS, CSS custom properties, CSS keyframe animations, Vite (build), vitest (tests).
Spec: docs/superpowers/specs/2026-03-28-cavai-theme-branding-design.md
Chunk 1: Commit Existing Changes
Before ANY Cavai theme work begins, all ~71 uncommitted file changes must be committed in small, logical groups. This ensures a clean git state for easy rollback of theme branding work.
The changelog at docs/superpowers/specs/2026-03-28-theme-polish-changelog.md documents all changes.
Task 1: Commit theme tokens and code editor themes
Files:
Stage:
src/styles/theme.cssStage:
src/utils/codemirror/theme-cavai-html.cssStage:
src/utils/aceEditor/theme-cavai-theme.js[ ] Step 1: Stage and commit
bash
git add src/styles/theme.css src/utils/codemirror/theme-cavai-html.css src/utils/aceEditor/theme-cavai-theme.js
git commit -m "Add revert tokens and refine theme colors across all themes"Task 2: Commit accessibility fixes
Files:
Stage:
src/components/common/RadioButton.vueStage:
src/pages/Chatbots/components/BuilderVisuals/Blocks/AddBlockTool.vueStage:
src/pages/Chatbots/components/BuilderVisuals/Blocks/BlockButton.vueStage:
src/pages/Chatbots/components/BuilderVisuals/Blocks/BlockActionsMenu.vueStage:
src/pages/Chatbots/components/BuilderVisuals/Blocks/BlockPanel.vue[ ] Step 1: Stage and commit
bash
git add src/components/common/RadioButton.vue \
src/pages/Chatbots/components/BuilderVisuals/Blocks/AddBlockTool.vue \
src/pages/Chatbots/components/BuilderVisuals/Blocks/BlockButton.vue \
src/pages/Chatbots/components/BuilderVisuals/Blocks/BlockActionsMenu.vue \
src/pages/Chatbots/components/BuilderVisuals/Blocks/BlockPanel.vue
git commit -m "Fix invisible text and icons in light themes"Task 3: Commit shortcuts dialog theming
Files:
Stage:
src/components/common/shortcuts/OsTypeToggle.vueStage:
src/components/common/shortcuts/ShortcutCategory.vueStage:
src/components/dialogs/ShortcutsDialog.vue[ ] Step 1: Stage and commit
bash
git add src/components/common/shortcuts/OsTypeToggle.vue \
src/components/common/shortcuts/ShortcutCategory.vue \
src/components/dialogs/ShortcutsDialog.vue
git commit -m "Theme shortcuts dialog for all themes"Task 4: Commit common component tokenization
Files:
Stage:
src/components/common/Card/Card.vueStage:
src/components/common/Card/CardSection.vueStage:
src/components/common/ColorPicker.vueStage:
src/components/common/Dropdown.vueStage:
src/components/common/InputField.vueStage:
src/components/common/InputSelect.vueStage:
src/components/common/TextArea.vueStage:
src/components/common/ToggleSwitch.vueStage:
src/components/common/TooltipWrapper.vue[ ] Step 1: Stage and commit
bash
git add src/components/common/Card/Card.vue \
src/components/common/Card/CardSection.vue \
src/components/common/ColorPicker.vue \
src/components/common/Dropdown.vue \
src/components/common/InputField.vue \
src/components/common/InputSelect.vue \
src/components/common/TextArea.vue \
src/components/common/ToggleSwitch.vue \
src/components/common/TooltipWrapper.vue
git commit -m "Tokenize common components for multi-theme support"Task 5: Commit revert UI unification
Files:
Stage:
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/TemplateButton.vueStage:
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/AnimationEffectList.vueStage:
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/AnimationEffectRow.vueStage:
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/AnimationConfigCard.vue[ ] Step 1: Stage and commit
bash
git add src/pages/Chatbots/components/BuilderVisuals/Configuration/components/TemplateButton.vue \
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/AnimationEffectList.vue \
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/AnimationEffectRow.vue \
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/AnimationConfigCard.vue
git commit -m "Unify revert indicator styling between templates and presets"Task 6: Commit animation preset persistence
Files:
Stage:
src/pages/Chatbots/components/BuilderVisuals/Blocks/data/types.tsStage:
src/pages/Chatbots/components/BuilderVisuals/Blocks/data/defaults.tsStage:
src/pages/Chatbots/components/BuilderVisuals/Configuration/mixins/configurationLogic.ts[ ] Step 1: Stage and commit
bash
git add src/pages/Chatbots/components/BuilderVisuals/Blocks/data/types.ts \
src/pages/Chatbots/components/BuilderVisuals/Blocks/data/defaults.ts \
src/pages/Chatbots/components/BuilderVisuals/Configuration/mixins/configurationLogic.ts
git commit -m "Persist animation lastPreset for revert across navigation"Task 7: Commit configuration section components
Files:
Stage:
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/BoxShadowSection.vueStage:
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/CodeEditor/CodeMirror/CodeMirrorEditor.vueStage:
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/CustomCSSEditor.vueStage:
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/DelaySection.vueStage:
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/DurationSection.vueStage:
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/FilterSection.vueStage:
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/FontSettings/FontSizeSection.vueStage:
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/HtmlSection.vueStage:
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/IterationsSection.vueStage:
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/MarginSection.vueStage:
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/MultiPostfixInput.vueStage:
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/OverrideIndicator.vueStage:
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/PlaybackSection.vueStage:
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/PositionSection.vueStage:
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/RotationSection.vueStage:
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/SlideSection.vueStage:
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/SlideSectionsPagination.vueStage:
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/TabNavigation.vueStage:
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/BorderRadius/CustomBorderRadius.vue[ ] Step 1: Stage and commit
bash
git add \
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/BoxShadowSection.vue \
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/CodeEditor/CodeMirror/CodeMirrorEditor.vue \
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/CustomCSSEditor.vue \
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/DelaySection.vue \
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/DurationSection.vue \
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/FilterSection.vue \
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/FontSettings/FontSizeSection.vue \
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/HtmlSection.vue \
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/IterationsSection.vue \
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/MarginSection.vue \
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/MultiPostfixInput.vue \
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/OverrideIndicator.vue \
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/PlaybackSection.vue \
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/PositionSection.vue \
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/RotationSection.vue \
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/SlideSection.vue \
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/SlideSectionsPagination.vue \
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/TabNavigation.vue \
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/BorderRadius/CustomBorderRadius.vue
git commit -m "Tokenize configuration section components"Task 8: Commit configuration panel and configs
Files:
Stage:
src/pages/Chatbots/components/BuilderVisuals/Configuration/ConfigurationPanel.vueStage:
src/pages/Chatbots/components/BuilderVisuals/Configuration/configs/HtmlConfiguration.vueStage:
src/pages/Chatbots/components/BuilderVisuals/Configuration/configs/SliderConfiguration.vue[ ] Step 1: Stage and commit
bash
git add src/pages/Chatbots/components/BuilderVisuals/Configuration/ConfigurationPanel.vue \
src/pages/Chatbots/components/BuilderVisuals/Configuration/configs/HtmlConfiguration.vue \
src/pages/Chatbots/components/BuilderVisuals/Configuration/configs/SliderConfiguration.vue
git commit -m "Refine configuration panel, code editor, and config layouts"Task 9: Commit flow editor changes
Files:
Stage:
src/pages/Chatbots/components/CavaiFlow/flowactors/OperatorBase.vueStage:
src/pages/Chatbots/components/CavaiFlow/NodeLabel.vueStage:
src/pages/Chatbots/components/CavaiFlow/brandingToolbarConstants.ts[ ] Step 1: Stage and commit
bash
git add src/pages/Chatbots/components/CavaiFlow/flowactors/OperatorBase.vue \
src/pages/Chatbots/components/CavaiFlow/NodeLabel.vue \
src/pages/Chatbots/components/CavaiFlow/brandingToolbarConstants.ts
git commit -m "Use dashed borders for branded operators, tokenize flow labels"Task 10: Commit builder delivery and option rows
Files:
Stage:
src/pages/Chatbots/components/BuilderDelivery/ImpressionPixelOptions.vueStage:
src/pages/Chatbots/components/BuilderDelivery/TagCode.vueStage:
src/pages/Chatbots/components/BuilderDelivery/VastOptions.vueStage:
src/pages/Chatbots/components/BuilderDelivery/VpaidOptions.vueStage:
src/pages/Chatbots/components/OptionRow/OptionRow.vueStage:
src/pages/Chatbots/components/OptionRow/OptionInputField.vueStage:
src/pages/Chatbots/components/OptionRow/OverrideControls.vue[ ] Step 1: Stage and commit
bash
git add src/pages/Chatbots/components/BuilderDelivery/ImpressionPixelOptions.vue \
src/pages/Chatbots/components/BuilderDelivery/TagCode.vue \
src/pages/Chatbots/components/BuilderDelivery/VastOptions.vue \
src/pages/Chatbots/components/BuilderDelivery/VpaidOptions.vue \
src/pages/Chatbots/components/OptionRow/OptionRow.vue \
src/pages/Chatbots/components/OptionRow/OptionInputField.vue \
src/pages/Chatbots/components/OptionRow/OverrideControls.vue
git commit -m "Tokenize delivery options and option row components"Task 11: Commit builder visuals misc and creative wizard
Files:
Stage:
src/pages/Chatbots/components/BuilderVisuals/FormatSelector.vueStage:
src/pages/Chatbots/components/BuilderVisuals/Preview/PreviewFormatSelector.vueStage:
src/pages/Chatbots/components/BuilderVisuals/Preview/PreviewPanel.vueStage:
src/components/CreativeWizard/CreativeTypeIcon.vueStage:
src/components/CreativeWizard/SizePreset.vueStage:
src/components/CreativeWizard/TemplateSelector/TemplateRow.vue[ ] Step 1: Stage and commit
bash
git add src/pages/Chatbots/components/BuilderVisuals/FormatSelector.vue \
src/pages/Chatbots/components/BuilderVisuals/Preview/PreviewFormatSelector.vue \
src/pages/Chatbots/components/BuilderVisuals/Preview/PreviewPanel.vue \
src/components/CreativeWizard/CreativeTypeIcon.vue \
src/components/CreativeWizard/SizePreset.vue \
src/components/CreativeWizard/TemplateSelector/TemplateRow.vue
git commit -m "Tokenize preview selectors and creative wizard components"Task 12: Commit global styles and remaining files
Files:
Stage:
src/styles/_components-builder.scssStage:
src/styles/_vuetify-reset.scssStage:
src/pages/Chatbots/ChatbotBuilder.vueStage:
src/assets/i18n/en.jsStage:
docs/theming-reference.mdStage:
docs/superpowers/specs/2026-03-28-theme-polish-changelog.mdStage:
src/assets/images/bubbles.png(mascot asset, needed by CavaiMascot in Chunk 2)[ ] Step 1: Stage and commit
bash
git add src/styles/_components-builder.scss \
src/styles/_vuetify-reset.scss \
src/pages/Chatbots/ChatbotBuilder.vue \
src/assets/i18n/en.js \
src/assets/images/bubbles.png \
docs/theming-reference.md \
docs/superpowers/specs/2026-03-28-theme-polish-changelog.md
git commit -m "Add global Vuetify resets, mascot asset, docs, and remaining polish"Task 13: Commit package.json changes
Files:
Stage:
package.jsonStage:
package-lock.json[ ] Step 1: Review changes to ensure no secrets or local file refs
bash
git diff HEAD -- package.jsonCheck that no file:../ local references are present. If they are, skip this commit and flag to user.
- [ ] Step 2: Stage and commit
bash
git add package.json package-lock.json
git commit -m "Update dependencies"Task 14: Verify clean git state
- [ ] Step 1: Verify no remaining changes
bash
git status
git diff --stat HEADExpected: Clean working tree (no modified, untracked, or staged files except the brainstorm directory which can be gitignored).
- [ ] Step 2: Verify build passes
bash
cd /Users/nicolay/CavaiProduct/Application-Frontend && npx vite build 2>&1 | tail -20Expected: Build succeeds without errors.
Chunk 2: Cavai Theme Foundation
After all existing changes are committed, this chunk creates the foundation: tokens, decorations file, and the CavaiMascot component.
Task 15: Add Cavai gradient and star tokens to theme.css
Files:
Modify:
src/styles/theme.css(inside[data-theme="cavai"]block)[ ] Step 1: Add tokens
Add these tokens to the end of the [data-theme="cavai"] block in theme.css (before the closing }):
css
/* Cavai decorative tokens — theme-exclusive, used only by _cavai-decorations.scss and CavaiMascot.vue */
--cavai-gradient: linear-gradient(135deg, #FF6B9E, #FFA07A, #FFDB58);
--cavai-gradient-subtle: linear-gradient(135deg, rgba(255,107,158,.06), rgba(255,160,122,.04), rgba(255,219,88,.02));
--cavai-gradient-accent: linear-gradient(135deg, rgba(255,107,158,.15), rgba(255,160,122,.10), rgba(255,219,88,.06));
--cavai-star-color: #FFDB58;
--cavai-star-glow: rgba(255,219,88,.3);- [ ] Step 2: Verify build
bash
cd /Users/nicolay/CavaiProduct/Application-Frontend && npx vite build 2>&1 | tail -5- [ ] Step 3: Commit
bash
git add src/styles/theme.css
git commit -m "Add Cavai gradient and star decoration tokens"Task 16: Create _cavai-decorations.scss
Files:
Create:
src/styles/_cavai-decorations.scssModify: The SCSS entry point that imports style partials (find the file that imports other
_*.scsspartials, likelysrc/styles/main.scssor similar)[ ] Step 1: Find the style import location
Style partials are imported in src/main.ts (around lines 12-20), not via SCSS @import. Find the last style import line:
- [ ] Step 2: Create the decorations file
Create src/styles/_cavai-decorations.scss with keyframes and star pseudo-elements:
scss
// Cavai theme decorative elements — stars, gradients, animations
// All rules scoped under [data-theme="cavai"] so they have zero effect on other themes.
// ─── Keyframes ───────────────────────────────────────────────
@keyframes cavai-twinkle {
0%, 100% { opacity: 0.3; }
50% { opacity: 1; }
}
@keyframes cavai-bob {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-4px); }
}
@keyframes cavai-glow-pulse {
0%, 100% { filter: drop-shadow(0 0 2px var(--cavai-star-glow)); }
50% { filter: drop-shadow(0 0 6px var(--cavai-star-glow)); }
}
@keyframes cavai-slide-in {
from { transform: translateY(100%); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
@keyframes cavai-star-burst {
0% { transform: scale(0); opacity: 1; }
70% { transform: scale(1.2); opacity: 0.8; }
100% { transform: scale(1); opacity: 0; }
}
// ─── Reduced motion ─────────────────────────────────────────
// Broad catch-all: disables ALL cavai animations including pseudo-element stars.
@media (prefers-reduced-motion: reduce) {
[data-theme="cavai"] {
*, *::before, *::after {
animation-duration: 0s !important;
animation-delay: 0s !important;
transition-duration: 0s !important;
}
}
}
// ─── Star utility class ─────────────────────────────────────
// Applied via _cavai-decorations.scss to elements that should show stars.
// Usage: add .cavai-star-container to a positioned parent. Stars appear via ::before/::after.
[data-theme="cavai"] {
.cavai-star {
color: var(--cavai-star-color);
font-size: 10px;
animation: cavai-twinkle 4s ease-in-out infinite;
pointer-events: none;
user-select: none;
}
.cavai-star--delayed {
animation-delay: 1.5s;
}
.cavai-star--slow {
animation-duration: 5s;
}
}- [ ] Step 3: Import in main.ts
Add import '@/styles/_cavai-decorations.scss' after the last style import (around line 20) in src/main.ts.
- [ ] Step 4: Verify build
bash
cd /Users/nicolay/CavaiProduct/Application-Frontend && npx vite build 2>&1 | tail -5- [ ] Step 5: Commit
bash
git add src/styles/_cavai-decorations.scss src/main.ts
git commit -m "Add Cavai decorations stylesheet with keyframes and star classes"Task 17: Create CavaiMascot.vue component
Files:
Create:
src/components/common/CavaiMascot/CavaiMascot.vue[ ] Step 1: Write test
Create src/components/common/__tests__/CavaiMascot.test.ts:
ts
import { describe, test, expect, beforeEach, vi } from 'vitest'
import { mountComponent } from '@/utils/testUtils'
import CavaiMascot from '../CavaiMascot/CavaiMascot.vue'
// Mock useTheme to control theme reactively
const mockThemeState = { theme: 'cavai' }
vi.mock('@/composables/useTheme', () => ({
useTheme: () => ({ state: mockThemeState }),
}))
describe('CavaiMascot', () => {
beforeEach(() => {
mockThemeState.theme = 'cavai'
})
test('renders when cavai theme is active', () => {
const wrapper = mountComponent(CavaiMascot)
expect(wrapper.find('.cavai-mascot').exists()).toBe(true)
})
test('renders nothing when theme is not cavai', () => {
mockThemeState.theme = 'dark'
const wrapper = mountComponent(CavaiMascot)
expect(wrapper.find('.cavai-mascot').exists()).toBe(false)
})
test('applies correct size class', () => {
const wrapper = mountComponent(CavaiMascot, { propsData: { size: 'lg' } })
expect(wrapper.find('.cavai-mascot--lg').exists()).toBe(true)
})
test('applies opacity style', () => {
const wrapper = mountComponent(CavaiMascot, { propsData: { opacity: 0.18 } })
const img = wrapper.find('.cavai-mascot__img')
expect(img.element.style.opacity).toBe('0.18')
})
test('applies mode class', () => {
const wrapper = mountComponent(CavaiMascot, { propsData: { mode: 'celebrate' } })
expect(wrapper.find('.cavai-mascot--celebrate').exists()).toBe(true)
})
})- [ ] Step 2: Run test to verify it fails
bash
cd /Users/nicolay/CavaiProduct/Application-Frontend && npx vitest run src/components/common/__tests__/CavaiMascot.test.ts --reporter=verbose 2>&1Expected: FAIL — module not found.
- [ ] Step 3: Create CavaiMascot.vue
Create src/components/common/CavaiMascot/CavaiMascot.vue:
vue
<template>
<div
v-if="isCavaiTheme"
:class="[
'cavai-mascot',
`cavai-mascot--${mode}`,
`cavai-mascot--${size}`,
{ 'cavai-mascot--hiding': isHiding },
]"
>
<img
src="@/assets/images/bubbles.png"
alt=""
class="cavai-mascot__img"
:style="{ opacity: effectiveOpacity }"
draggable="false"
>
</div>
</template>
<script lang="ts">
import { useTheme } from '@/composables/useTheme'
const AUTO_HIDE_MODES = ['celebrate']
const AUTO_HIDE_DURATION = 3000
const FADE_OUT_DURATION = 500
export default {
name: 'CavaiMascot',
props: {
mode: {
type: String,
default: 'peek',
validator: (v: string) => ['peek', 'celebrate', 'wave', 'think', 'upload'].includes(v),
},
size: {
type: String,
default: 'md',
validator: (v: string) => ['sm', 'md', 'lg'].includes(v),
},
opacity: {
type: Number,
default: 1,
},
dragState: {
type: String,
default: 'idle',
validator: (v: string) => ['idle', 'over', 'dropped'].includes(v),
},
},
data() {
return {
themeState: useTheme().state,
isHiding: false,
hideTimer: null as ReturnType<typeof setTimeout> | null,
}
},
computed: {
isCavaiTheme(): boolean {
return this.themeState.theme === 'cavai'
},
effectiveOpacity(): number {
if (this.isHiding) return 0
return this.opacity
},
},
watch: {
mode: {
immediate: true,
handler(newMode: string) {
this.clearTimer()
this.isHiding = false
if (AUTO_HIDE_MODES.includes(newMode)) {
this.hideTimer = setTimeout(() => {
this.isHiding = true
setTimeout(() => this.$emit('hidden'), FADE_OUT_DURATION)
}, AUTO_HIDE_DURATION)
}
},
},
},
beforeDestroy() {
this.clearTimer()
},
methods: {
clearTimer() {
if (this.hideTimer) {
clearTimeout(this.hideTimer)
this.hideTimer = null
}
},
},
}
</script>
<style scoped lang="scss">
.cavai-mascot {
display: flex;
align-items: center;
justify-content: center;
pointer-events: none;
user-select: none;
&__img {
display: block;
transition: opacity 0.5s ease-out;
}
// ─── Sizes ───
&--sm .cavai-mascot__img { width: 32px; }
&--md .cavai-mascot__img { width: 64px; }
&--lg .cavai-mascot__img { width: 128px; }
// ─── Modes ───
&--peek {
.cavai-mascot__img {
animation: cavai-bob 4s ease-in-out infinite;
}
}
&--celebrate {
.cavai-mascot__img {
animation: cavai-slide-in 0.4s ease-out;
}
}
&--wave {
.cavai-mascot__img {
animation: cavai-bob 3s ease-in-out infinite;
}
}
&--think {
.cavai-mascot__img {
animation: cavai-glow-pulse 3s ease-in-out infinite;
}
}
&--upload {
.cavai-mascot__img {
animation: cavai-bob 4s ease-in-out infinite;
transition: transform 0.3s ease-out;
}
}
// ─── Hiding (auto-hide fade-out) ───
&--hiding .cavai-mascot__img {
opacity: 0 !important;
}
}
// Note: reduced-motion handled globally in _cavai-decorations.scss
</style>- [ ] Step 4: Run tests
bash
cd /Users/nicolay/CavaiProduct/Application-Frontend && npx vitest run src/components/common/__tests__/CavaiMascot.test.ts --reporter=verbose 2>&1Expected: All 5 tests PASS.
- [ ] Step 5: Verify build
bash
cd /Users/nicolay/CavaiProduct/Application-Frontend && npx vite build 2>&1 | tail -5- [ ] Step 6: Commit
bash
git add src/components/common/CavaiMascot/CavaiMascot.vue \
src/components/common/__tests__/CavaiMascot.test.ts
git commit -m "Add CavaiMascot component with mode-driven poses and auto-hide"Chunk 3: Phase 1 Placements (Refactoring Existing Code)
These tasks modify existing mascot integrations to use the new CavaiMascot component.
Task 18: Refactor ConfigurationPanel to use CavaiMascot
Files:
Modify:
src/pages/Chatbots/components/BuilderVisuals/Configuration/ConfigurationPanel.vue[ ] Step 1: Read the current file
Read ConfigurationPanel.vue to understand the current mascot markup and styles.
- [ ] Step 2: Replace raw img with CavaiMascot
In the template, replace:
html
<div class="mascot-peek">
<img src="@/assets/images/bubbles.png" alt="" class="mascot-img">
</div>With:
html
<CavaiMascot mode="peek" size="lg" :opacity="0.18" />- [ ] Step 3: Add component import and registration
In the <script> section, add:
ts
import CavaiMascot from '@/components/common/CavaiMascot/CavaiMascot.vue'Add to components: { CavaiMascot }.
- [ ] Step 4: Remove old mascot CSS
Remove from <style>:
The
.mascot-peekclass and its nested rulesThe
.mascot-imgclassThe
@keyframes mascot-bobdefinitionThe
[data-theme="cavai"] &wrapper for mascot opacity[ ] Step 5: Verify build and visual check
bash
cd /Users/nicolay/CavaiProduct/Application-Frontend && npx vite build 2>&1 | tail -5Start dev server and verify: switch to Cavai theme, select no block → mascot should appear at 18% opacity with bobbing animation. Switch to dark theme → mascot should disappear.
- [ ] Step 6: Commit
bash
git add src/pages/Chatbots/components/BuilderVisuals/Configuration/ConfigurationPanel.vue
git commit -m "Refactor config panel mascot to use CavaiMascot component"Task 19: Enhance login page with stars and gradient
Files:
Modify:
src/styles/_pages.scss[ ] Step 1: Read the current signin styles
Read _pages.scss and find the .signin_page section (around lines 700-760).
- [ ] Step 2: Add cavai-specific decorations
After the existing .signin_page__bubbles styles, add cavai-scoped star decorations:
scss
// Cavai theme enhancements for login page
[data-theme="cavai"] {
.signin_page {
// Subtle gradient wash over the page background
&::before {
content: '';
position: fixed;
inset: 0;
background: var(--cavai-gradient-subtle);
pointer-events: none;
z-index: 0;
}
// Decorative stars near the bubbles mascot
&__bubbles::before,
&__bubbles::after {
position: absolute;
font-size: 10px;
color: var(--cavai-star-color);
pointer-events: none;
z-index: 1;
}
&__bubbles::before {
content: '✦';
top: -8px;
right: 10px;
animation: cavai-twinkle 4s ease-in-out infinite;
}
&__bubbles::after {
content: '✦';
top: 5px;
left: -12px;
font-size: 7px;
animation: cavai-twinkle 5s ease-in-out infinite 1.5s;
}
}
}- [ ] Step 3: Verify build
bash
cd /Users/nicolay/CavaiProduct/Application-Frontend && npx vite build 2>&1 | tail -5- [ ] Step 4: Visual check
Navigate to login page with Cavai theme → verify subtle gradient wash and twinkling stars near Bubbles.
- [ ] Step 5: Commit
bash
git add src/styles/_pages.scss
git commit -m "Add star decorations and gradient wash to Cavai login page"Chunk 4: Phase 2 Placements (New Integrations)
These tasks add mascot to new locations that don't currently have one.
Task 20: Add mascot to publish success (BuildProgress.vue)
Files:
Modify:
src/pages/Chatbots/components/BuildProgress.vue[ ] Step 1: Read BuildProgress.vue
Understand the current completion state rendering — what happens when build finishes successfully.
- [ ] Step 2: Add CavaiMascot celebrate on build complete
In the template, add a mascot that appears when build reaches 100%:
html
<CavaiMascot
v-if="buildComplete"
mode="celebrate"
size="md"
@hidden="onMascotHidden"
/>Add component import, registration, and the onMascotHidden method. The exact integration depends on the current template structure — adapt to fit.
- [ ] Step 3: Verify build and visual check
Build a creative in Cavai theme → mascot should slide in on success, disappear after 3-4s.
- [ ] Step 4: Commit
bash
git add src/pages/Chatbots/components/BuildProgress.vue
git commit -m "Add Bubbles celebration on successful publish in Cavai theme"Task 21: Add mascot to flow editor empty state
Files:
Modify:
src/pages/Chatbots/components/CavaiFlow/CavaiFlow.vue[ ] Step 1: Read CavaiFlow.vue
Find where operators are rendered and how an empty state could be detected (e.g., empty operators array).
- [ ] Step 2: Add empty state with mascot
When operator list is empty, show:
html
<div v-if="isFlowEmpty" class="cavai-flow-empty">
<CavaiMascot mode="wave" size="lg" />
<p class="cavai-flow-empty__text">Dra inn din første operator!</p>
</div>Style the empty state centered in the canvas. Add isFlowEmpty computed property based on operator count. Add component import and registration.
- [ ] Step 3: Verify build and visual check
Open flow editor with no operators in Cavai theme → mascot should appear with wave animation.
- [ ] Step 4: Commit
bash
git add src/pages/Chatbots/components/CavaiFlow/CavaiFlow.vue
git commit -m "Add Bubbles wave mascot to empty flow editor in Cavai theme"Task 22: Add mascot to empty lists (MainTable.vue)
Files:
Modify:
src/components/common/MainTable.vue[ ] Step 1: Read MainTable.vue
Find the Vuetify v-data-table and its existing empty state handling.
- [ ] Step 2: Add CavaiMascot in no-data slot
Use Vuetify's no-data slot:
html
<template v-slot:no-data>
<div class="main-table-empty">
<CavaiMascot mode="peek" size="sm" />
<span class="main-table-empty__text">{{ $t('noData') }}</span>
</div>
</template>The CavaiMascot auto-hides on non-cavai themes. Add component import and registration. Style the empty state.
- [ ] Step 3: Verify build and visual check
Navigate to an empty creative list in Cavai theme → mascot peek with text.
- [ ] Step 4: Commit
bash
git add src/components/common/MainTable.vue
git commit -m "Add Bubbles peek to empty table states in Cavai theme"Chunk 5: Phase 3 Placements (Interactive)
Task 23: Add mascot to upload/drag-drop zones
Files:
Modify:
src/components/common/FileUploader.vueModify:
src/components/common/ImageInput.vue[ ] Step 1: Read FileUploader.vue and ImageInput.vue
Understand the drag-and-drop handling: where dragover/dragleave/drop events are handled, what state tracks drag activity. ImageInput.vue wraps FileUploader.vue — determine if the mascot should be added at the FileUploader level (inherited by both) or needs separate integration in ImageInput.
- [ ] Step 2: Add CavaiMascot with dragState prop to FileUploader
Add mascot to the drop zone area, wired to existing drag state:
html
<CavaiMascot
mode="upload"
size="md"
:drag-state="isDragging ? 'over' : (justDropped ? 'dropped' : 'idle')"
/>Add justDropped data property, set it to true on drop, reset after 1s. Add component import and registration. Adapt to the actual drag state mechanism in the component.
- [ ] Step 3: Add to ImageInput if needed
If ImageInput.vue has its own drop zone separate from FileUploader, add the same mascot integration there. If it simply wraps FileUploader, no additional work needed.
- [ ] Step 4: Verify build and visual check
In Cavai theme, drag a file over an upload area → mascot bounces. Drop → short celebrate. Test both generic file upload and image upload areas.
- [ ] Step 5: Commit
bash
git add src/components/common/FileUploader.vue src/components/common/ImageInput.vue
git commit -m "Add Bubbles mascot reactions to file upload drag-and-drop"Task 24: Add mascot to loading spinners
Files:
Modify:
src/pages/Chatbots/ChatbotBuilder.vue(start with main builder loading)[ ] Step 1: Read ChatbotBuilder.vue
Find the v-progress-circular usage and its loading condition.
- [ ] Step 2: Add conditional CavaiMascot
Replace or wrap the spinner with a conditional:
html
<template v-if="isCavaiTheme">
<CavaiMascot mode="think" size="md" />
</template>
<v-progress-circular v-else ... />Use useTheme to determine isCavaiTheme. Add component import and registration.
- [ ] Step 3: Verify build and visual check
Trigger loading state in Cavai theme → thinking mascot instead of spinner.
- [ ] Step 4: Commit
bash
git add src/pages/Chatbots/ChatbotBuilder.vue
git commit -m "Replace loading spinner with Bubbles think animation in Cavai theme"Chunk 6: Gradient Accents on UI Elements
Task 25: Add gradient accents to Cavai theme UI
Files:
Modify:
src/styles/_cavai-decorations.scss(add gradient rules)Possibly modify:
src/pages/Chatbots/components/BuilderVisuals/Configuration/components/TabNavigation.vue[ ] Step 1: Add gradient accent rules to _cavai-decorations.scss
scss
// ─── Gradient accents ────────────────────────────────────────
[data-theme="cavai"] {
// Active tab underline — full gradient
.tab-navigation .tab-item.active::after {
background: var(--cavai-gradient);
}
// Card section subtle gradient wash
.card {
background-image: var(--cavai-gradient-subtle);
background-size: 100% 100%;
}
}The exact selectors depend on the current component class names — read the relevant files first and adapt. Keep it minimal — only add gradient accents where they're clearly decorative and don't interfere with readability.
- [ ] Step 2: Verify build and visual check
Check that gradient accents appear in Cavai theme and are absent in other themes.
- [ ] Step 3: Commit
bash
git add src/styles/_cavai-decorations.scss
git commit -m "Add gradient accents to tabs and cards in Cavai theme"Task 26: Add star decorations to card headers
Files:
Modify:
src/styles/_cavai-decorations.scss[ ] Step 1: Add card header star decorations
scss
// ─── Card header stars ───────────────────────────────────────
[data-theme="cavai"] {
.title-section {
position: relative;
&::after {
content: '✦';
position: absolute;
right: 0;
top: 0;
color: var(--cavai-star-color);
font-size: 8px;
opacity: 0.5;
animation: cavai-twinkle 5s ease-in-out infinite;
pointer-events: none;
}
}
}This targets the .title-section from Card.vue which wraps card section headers.
- [ ] Step 2: Verify build and visual check
Check that card headers show twinkling stars in Cavai theme. Verify they don't appear in other themes.
- [ ] Step 3: Commit
bash
git add src/styles/_cavai-decorations.scss
git commit -m "Add twinkling star decorations to card headers in Cavai theme"Task 27: Final verification and cleanup
- [ ] Step 1: Run full test suite
bash
cd /Users/nicolay/CavaiProduct/Application-Frontend && npx vitest run --reporter=verbose 2>&1Expected: All tests pass.
- [ ] Step 2: Run build
bash
cd /Users/nicolay/CavaiProduct/Application-Frontend && npx vite build 2>&1 | tail -10Expected: Clean build.
- [ ] Step 3: Visual QA checklist
Switch through all 5 themes and verify:
- [ ] Cavai theme: stars twinkle, mascot bobs, gradient accents visible
- [ ] Dark theme: no stars, no mascot, no gradients, no visual changes
- [ ] Light theme: same as dark — no cavai elements
- [ ] Nord theme: same — no cavai elements
- [ ] Mocha theme: same — no cavai elements
Check the 7 mascot placements in Cavai theme:
[ ] Config panel empty state: mascot at 18% opacity with bob
[ ] Publish success: mascot slides in, auto-hides
[ ] Flow editor empty: mascot waves in empty canvas
[ ] Upload drag-drop: mascot reacts to drag events
[ ] Empty lists: mascot peek in empty tables
[ ] Loading spinner: think animation replaces spinner
[ ] Login page: stars twinkle near bubbles, gradient wash
[ ] Step 4: Check prefers-reduced-motion
In browser devtools, enable prefers-reduced-motion: reduce. Verify all cavai animations stop — mascot shows static pose, stars don't twinkle.