Skip to main content

Style Customization

Vue 3 Widget provides multi-level style customization capabilities, from simple class name overrides to fully custom rendering.

Customization Levels

LevelMethodUse Case
1CSS class name overrideAdjust colors, spacing, fonts, etc.
2style propertyInline styles for individual components
3Custom icons (slots)Replace built-in icons
4scoped slots renderingFully custom component structure

CSS Class Name Override

Naming Convention

All components use BEM naming convention, prefixed with apk- (AaaS Pilot Kit):

.apk-{component-name}                    /* Block */
.apk-{component-name}__{element-name} /* Element */
.apk-{component-name}--{modifier} /* Modifier */

Example

/* Modify control panel background */
.apk-control-panel {
background: rgba(0, 0, 0, 0.8);
}

/* Modify input style */
.apk-control-panel__input {
border: 2px solid #007bff;
border-radius: 25px;
}

/* Modify mobile button size */
.apk-control-panel--mobile .apk-control-panel__mic-btn {
width: 60px;
height: 60px;
}

Component Class Name Quick Reference

PilotKit

Class NameDescription
.apk-pilot-kitRoot container
.apk-pilot-kit__contentContent area

ConversationList

Class NameDescription
.apk-conversation-listRoot container
.apk-conversation-list--mobileMobile style
.apk-conversation-list--desktopDesktop style
.apk-conversation-list__innerInner scroll container
.apk-conversation-list__follow-buttonScroll follow button

Conversation

Class NameDescription
.apk-conversationRoot container
.apk-conversation--clientUser message
.apk-conversation--aiAI message
.apk-conversation__content-textText content

ControlPanel

Class NameDescription
.apk-control-panelRoot container
.apk-control-panel--mobileMobile style
.apk-control-panel__innerInner container
.apk-control-panel__mode-btnMode switch button
.apk-control-panel__inputInput
.apk-control-panel__send-btnSend button
.apk-control-panel__mic-btnMicrophone button
.apk-control-panel__hangup-btnHangup button

Input

Class NameDescription
.apk-inputRoot container
.apk-input--mobileMobile style
.apk-input--desktopDesktop style
.apk-input__fieldInput field
.apk-input__send-iconSend icon
.apk-input--errorError state
.apk-input--disabledDisabled state

More Class Names

See each component documentation's "Style Customization" section for details.

class Property

Each component supports class property, used to add custom class names:

<template>
<ControlPanel class="my-control-panel" />
</template>

<style>
.my-control-panel {
margin-bottom: 20px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
</style>

style Property

Use style property to set inline styles:

<template>
<ConversationList
:style="{
maxHeight: '400px',
borderRadius: '16px',
backgroundColor: 'rgba(255, 255, 255, 0.9)'
}"
/>
</template>

Custom Icons (Slots)

Multiple components support customizing icons through slots:

ControlPanel Icons

<template>
<ControlPanel>
<template #muteIcon><MuteIcon /></template>
<template #unmuteIcon><UnmuteIcon /></template>
<template #hangupIcon><HangupIcon /></template>
<template #asrIcon><MicIcon /></template>
<template #keyboardIcon><KeyboardIcon /></template>
<template #sendIcon><SendIcon /></template>
</ControlPanel>
</template>

ConversationList Icons

<template>
<ConversationList>
<template #closeIcon><CloseIcon /></template>
</ConversationList>
</template>

RecommendedQuestions Icons

<template>
<RecommendedQuestions>
<template #arrowIcon><ArrowIcon /></template>
</RecommendedQuestions>
</template>

Scoped Slots Rendering

You can fully customize component rendering through scoped slots:

ConversationList

<template>
<ConversationList v-slot="{conversations, latestConversation}">
<div class="my-conversation-list">
<h3>Conversation History</h3>
<div
v-for="conv in conversations"
:key="conv.id"
:class="['my-message', `my-message--${conv.type}`]"
>
<span class="speaker">
{{ conv.type === 'client' ? 'Me' : 'AI' }}
</span>
<p>{{ conv.text }}</p>
</div>
</div>
</ConversationList>
</template>

Conversation

<template>
<Conversation
:conversation="conv"
v-slot="{contents, fullText, type, isCompleted}"
>
<div :class="['custom-bubble', type]">
<p>{{ fullText }}</p>
<span v-if="!isCompleted" class="typing-indicator">...</span>
</div>
</Conversation>
</template>

Subtitle

<template>
<Subtitle v-slot="{speakerName, fullText, isExpanded, toggleExpand}">
<div class="custom-subtitle">
<strong>{{ speakerName }}:</strong>
<span>{{ fullText }}</span>
<button @click="toggleExpand">
{{ isExpanded ? 'Collapse' : 'Expand' }}
</button>
</div>
</Subtitle>
</template>

Input

<template>
<Input v-slot="{value, setValue, handleSend, disabled, loading}">
<div class="custom-input">
<textarea
:value="value"
@input="e => setValue(e.target.value)"
:disabled="disabled"
placeholder="Enter message..."
/>
<button @click="handleSend" :disabled="loading || !value">
{{ loading ? 'Sending...' : 'Send' }}
</button>
</div>
</Input>
</template>

StartupScreen

<template>
<StartupScreen :show="showStartup" v-slot="{onStart, isActivating}">
<div class="custom-startup">
<img src="/logo.png" alt="Logo" />
<h1>Welcome to Digital Employee</h1>
<button @click="onStart" :disabled="isActivating">
{{ isActivating ? 'Activating...' : 'Start Conversation' }}
</button>
</div>
</StartupScreen>
</template>

CSS Variables (Planned)

Note

CSS variable support is being planned, future versions will provide more convenient theme customization methods.

Expected variable examples:

:root {
--apk-primary-color: #007bff;
--apk-background-color: rgba(255, 255, 255, 0.9);
--apk-text-color: #333;
--apk-border-radius: 20px;
--apk-font-family: -apple-system, BlinkMacSystemFont, sans-serif;
}

Theme Examples

Dark Theme

/* Dark theme */
.dark-theme .apk-conversation-list {
background: rgba(30, 30, 30, 0.95);
}

.dark-theme .apk-conversation--ai {
background: rgba(50, 50, 50, 0.9);
color: #fff;
}

.dark-theme .apk-conversation--client {
background: #007bff;
color: #fff;
}

.dark-theme .apk-control-panel {
background: rgba(40, 40, 40, 0.9);
}

.dark-theme .apk-control-panel__input {
background: rgba(60, 60, 60, 0.9);
color: #fff;
border-color: rgba(100, 100, 100, 0.5);
}

Rounded Theme

/* Rounded theme */
.rounded-theme .apk-conversation {
border-radius: 24px;
}

.rounded-theme .apk-control-panel__input {
border-radius: 30px;
}

.rounded-theme .apk-control-panel__mic-btn,
.rounded-theme .apk-control-panel__send-btn {
border-radius: 50%;
}

Corporate Theme

/* Corporate brand theme */
.corporate-theme .apk-conversation--ai {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #fff;
}

.corporate-theme .apk-control-panel__mic-btn {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}

.corporate-theme .apk-subtitle {
border-left: 3px solid #667eea;
padding-left: 12px;
}

Best Practices

1. Use Scoped Styles

Vue SFC scoped styles avoid global pollution:

<template>
<ControlPanel class="my-control-panel" />
</template>

<style scoped>
.my-control-panel {
/* Styles */
}

/* Deep selectors for overriding child component styles */
:deep(.apk-control-panel__input) {
border-color: #007bff;
}
</style>

2. Keep Original Class Names

When overriding styles, keep original class names to ensure responsive and other features work correctly:

<template>
<!-- ✅ Recommended -->
<ControlPanel class="my-custom-class" />

<!-- ❌ Avoid: Completely replacing internal structure causing feature loss -->
</template>

3. Prioritize CSS Overrides

For simple style adjustments, prioritize CSS overrides over scoped slots rendering:

/* ✅ Recommended: Simple style adjustments */
.apk-conversation--ai {
background: #f0f0f0;
}
<template>
<!-- ❌ Overused: Simple styles don't need custom rendering -->
<Conversation :conversation="conv" v-slot="{fullText}">
<div style="background: #f0f0f0">{{ fullText }}</div>
</Conversation>
</template>

4. Progressive Customization

Start with the simplest approach, upgrade as needed:

  1. First try class + CSS
  2. If not enough, try style property
  3. Use scoped slots rendering when structural changes are needed