Ask0 logoAsk0

Customising

Advanced customization for Ask0's UI including themes, layouts, and behavior. Tailor the AI assistant experience to your needs.

Take full control of Ask0's appearance and behavior with advanced customization options. From custom CSS to component overrides, create a completely unique experience.

Custom CSS

CSS Variables

Override CSS custom properties:

/* Global CSS variables */
:root {
  /* Colors */
  --ask0-primary: #0066CC;
  --ask0-primary-foreground: #FFFFFF;
  --ask0-secondary: #F0F4F8;
  --ask0-secondary-foreground: #000000;
  --ask0-accent: #00AA55;
  --ask0-muted: #F5F5F5;
  --ask0-border: #E0E0E0;

  /* Typography */
  --ask0-font-sans: 'Inter', system-ui, sans-serif;
  --ask0-font-mono: 'SF Mono', Monaco, monospace;
  --ask0-font-size-xs: 0.75rem;
  --ask0-font-size-sm: 0.875rem;
  --ask0-font-size-base: 1rem;
  --ask0-font-size-lg: 1.125rem;
  --ask0-font-size-xl: 1.25rem;

  /* Spacing */
  --ask0-radius: 0.5rem;
  --ask0-radius-sm: 0.25rem;
  --ask0-radius-lg: 1rem;
  --ask0-spacing-xs: 0.25rem;
  --ask0-spacing-sm: 0.5rem;
  --ask0-spacing-md: 1rem;
  --ask0-spacing-lg: 1.5rem;
  --ask0-spacing-xl: 2rem;

  /* Animations */
  --ask0-animation-duration: 200ms;
  --ask0-animation-timing: ease-in-out;

  /* Shadows */
  --ask0-shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
  --ask0-shadow-md: 0 4px 6px rgba(0,0,0,0.1);
  --ask0-shadow-lg: 0 10px 15px rgba(0,0,0,0.1);
}

/* Dark mode */
[data-theme="dark"] {
  --ask0-primary: #4DA6FF;
  --ask0-background: #1A1A1A;
  --ask0-foreground: #FFFFFF;
  --ask0-border: #333333;
}

Component Styling

Target specific components:

/* Widget container */
.ask0-widget {
  border: 2px solid var(--ask0-border);
  box-shadow: var(--ask0-shadow-lg);
  backdrop-filter: blur(10px);
}

/* Header customization */
.ask0-header {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  font-weight: 600;
}

/* Message bubbles */
.ask0-message--user {
  background: var(--ask0-primary);
  color: var(--ask0-primary-foreground);
  border-radius: 18px 18px 4px 18px;
}

.ask0-message--assistant {
  background: var(--ask0-muted);
  color: var(--ask0-foreground);
  border-radius: 18px 18px 18px 4px;
}

/* Input field */
.ask0-input {
  border: 2px solid transparent;
  background: var(--ask0-muted);
  transition: all 0.3s ease;
}

.ask0-input:focus {
  border-color: var(--ask0-primary);
  background: white;
  box-shadow: 0 0 0 3px rgba(0,102,204,0.1);
}

/* Custom animations */
@keyframes slideInUp {
  from {
    transform: translateY(100%);
    opacity: 0;
  }
  to {
    transform: translateY(0);
    opacity: 1;
  }
}

.ask0-widget--opening {
  animation: slideInUp 0.3s ease-out;
}

Component Overrides

Custom React Components

Replace default components:

import { Ask0Provider, Ask0Widget } from '@ask0/react';

// Custom header component
const CustomHeader = ({ onClose, onMinimize }) => (
  <div className="custom-header">
    <img src="/logo.png" alt="Logo" />
    <h2>AI Assistant</h2>
    <button onClick={onClose}>×</button>
  </div>
);

// Custom message component
const CustomMessage = ({ message, isUser }) => (
  <div className={`custom-message ${isUser ? 'user' : 'assistant'}`}>
    {message.content}
    {message.sources && (
      <div className="sources">
        {message.sources.map(source => (
          <a href={source.url}>{source.title}</a>
        ))}
      </div>
    )}
  </div>
);

// Custom input component
const CustomInput = ({ onSend }) => {
  const [value, setValue] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    onSend(value);
    setValue('');
  };

  return (
    <form onSubmit={handleSubmit} className="custom-input">
      <input
        value={value}
        onChange={(e) => setValue(e.target.value)}
        placeholder="Ask me anything..."
      />
      <button type="submit">Send</button>
    </form>
  );
};

// Use custom components
function App() {
  return (
    <Ask0Provider>
      <Ask0Widget
        components={{
          header: CustomHeader,
          message: CustomMessage,
          input: CustomInput
        }}
      />
    </Ask0Provider>
  );
}

Template Overrides

Override HTML templates:

window.ask0.templates = {
  // Message template
  message: (data) => `
    <div class="message ${data.isUser ? 'user' : 'assistant'}">
      <img src="${data.avatar}" alt="Avatar" />
      <div class="content">
        <div class="text">${data.content}</div>
        <div class="meta">
          <span class="time">${data.time}</span>
          ${data.sources ? `<span class="sources">${data.sources.length} sources</span>` : ''}
        </div>
      </div>
    </div>
  `,

  // Input template
  input: () => `
    <div class="input-wrapper">
      <textarea placeholder="Type here..."></textarea>
      <button class="send">→</button>
      <button class="voice">🎤</button>
    </div>
  `,

  // Loading template
  loading: () => `
    <div class="loading">
      <div class="dots">
        <span></span>
        <span></span>
        <span></span>
      </div>
      <span class="text">Thinking...</span>
    </div>
  `
};

Layout Customization

Grid Layout

Create custom layouts:

/* Custom grid layout */
.ask0-widget {
  display: grid;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header"
    "messages"
    "input";
  gap: 0;
}

.ask0-header {
  grid-area: header;
  position: sticky;
  top: 0;
  z-index: 10;
}

.ask0-messages {
  grid-area: messages;
  overflow-y: auto;
  padding: var(--ask0-spacing-md);
}

.ask0-input-area {
  grid-area: input;
  position: sticky;
  bottom: 0;
  background: white;
  border-top: 1px solid var(--ask0-border);
}

/* Sidebar layout variant */
.ask0-widget--sidebar {
  grid-template-columns: 200px 1fr;
  grid-template-areas:
    "sidebar header"
    "sidebar messages"
    "sidebar input";
}

.ask0-sidebar {
  grid-area: sidebar;
  background: var(--ask0-muted);
  padding: var(--ask0-spacing-md);
}

Responsive Adjustments

Adapt to different screen sizes:

/* Mobile adjustments */
@media (max-width: 768px) {
  .ask0-widget {
    position: fixed !important;
    inset: 0 !important;
    width: 100% !important;
    height: 100% !important;
    border-radius: 0 !important;
  }

  .ask0-messages {
    padding: var(--ask0-spacing-sm);
  }

  .ask0-message {
    max-width: 85% !important;
  }
}

/* Tablet adjustments */
@media (min-width: 768px) and (max-width: 1024px) {
  .ask0-widget {
    width: 450px;
    height: 650px;
  }
}

/* Large screens */
@media (min-width: 1440px) {
  .ask0-widget {
    width: 420px;
    height: 680px;
  }

  .ask0-message {
    font-size: 1.05rem;
  }
}

Behavior Customization

Custom Interactions

Define custom user interactions:

// Custom keyboard shortcuts
window.ask0.shortcuts = {
  'cmd+k': () => window.ask0.open(),
  'cmd+/': () => window.ask0.toggle(),
  'escape': () => window.ask0.close(),
  'cmd+enter': () => window.ask0.send(),
  'up': () => window.ask0.previousMessage(),
  'down': () => window.ask0.nextMessage()
};

// Custom gestures (mobile)
window.ask0.gestures = {
  swipeDown: () => window.ask0.minimize(),
  swipeLeft: () => window.ask0.close(),
  pinch: () => window.ask0.toggleFullscreen(),
  doubleTap: () => window.ask0.clearMessages()
};

// Custom triggers
window.ask0.triggers = {
  // Click trigger
  click: {
    selector: '.help-button, .support-link',
    action: 'open'
  },

  // Hover trigger
  hover: {
    selector: '.tooltip',
    delay: 1000,
    action: 'showHint'
  },

  // Scroll trigger
  scroll: {
    percent: 75,
    action: 'suggest'
  }
};

State Management

Manage widget state:

// Custom state manager
class Ask0StateManager {
  constructor() {
    this.state = {
      isOpen: false,
      messages: [],
      user: null,
      context: {}
    };
  }

  // State persistence
  persist() {
    localStorage.setItem('ask0_state', JSON.stringify(this.state));
  }

  restore() {
    const saved = localStorage.getItem('ask0_state');
    if (saved) {
      this.state = JSON.parse(saved);
    }
  }

  // State hooks
  beforeOpen() {
    // Custom logic before opening
    analytics.track('Widget Opening');
  }

  afterClose() {
    // Custom logic after closing
    this.persist();
  }

  onMessage(message) {
    // Custom message handling
    this.state.messages.push(message);
    this.persist();
  }
}

// Apply custom state manager
window.ask0.stateManager = new Ask0StateManager();

Advanced Theming

Dynamic Themes

Change themes programmatically:

// Theme manager
const themeManager = {
  themes: {
    light: {
      primary: '#0066CC',
      background: '#FFFFFF',
      foreground: '#000000'
    },
    dark: {
      primary: '#4DA6FF',
      background: '#1A1A1A',
      foreground: '#FFFFFF'
    },
    custom: {
      primary: '#FF6B6B',
      background: '#FFF5F5',
      foreground: '#2D3748'
    }
  },

  apply(themeName) {
    const theme = this.themes[themeName];
    Object.entries(theme).forEach(([key, value]) => {
      document.documentElement.style.setProperty(`--ask0-${key}`, value);
    });
    window.ask0.theme = themeName;
  },

  // Auto theme based on time
  autoTheme() {
    const hour = new Date().getHours();
    const theme = hour >= 6 && hour < 18 ? 'light' : 'dark';
    this.apply(theme);
  },

  // Match system theme
  matchSystem() {
    const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
    this.apply(prefersDark ? 'dark' : 'light');
  }
};

Theme Builder

Create themes visually:

// Theme builder utility
class Ask0ThemeBuilder {
  constructor() {
    this.theme = {};
  }

  setColors(colors) {
    this.theme.colors = colors;
    return this;
  }

  setTypography(typography) {
    this.theme.typography = typography;
    return this;
  }

  setSpacing(spacing) {
    this.theme.spacing = spacing;
    return this;
  }

  setShadows(shadows) {
    this.theme.shadows = shadows;
    return this;
  }

  build() {
    return this.theme;
  }

  export() {
    return JSON.stringify(this.theme, null, 2);
  }

  import(themeString) {
    this.theme = JSON.parse(themeString);
    return this;
  }
}

// Create custom theme
const customTheme = new Ask0ThemeBuilder()
  .setColors({
    primary: '#FF6B6B',
    secondary: '#4ECDC4'
  })
  .setTypography({
    fontFamily: 'Helvetica Neue',
    fontSize: '16px'
  })
  .build();

window.ask0.setTheme(customTheme);

Plugin System

Creating Plugins

Extend functionality with plugins:

// Plugin template
class Ask0Plugin {
  constructor(name, options = {}) {
    this.name = name;
    this.options = options;
  }

  // Lifecycle hooks
  onInit(widget) {
    console.log(`${this.name} plugin initialized`);
  }

  onOpen(widget) {
    // Widget opened
  }

  onMessage(message, widget) {
    // Message sent/received
  }

  onClose(widget) {
    // Widget closed
  }

  // Custom methods
  enhance(widget) {
    // Add custom functionality
  }
}

// Example: Analytics plugin
class AnalyticsPlugin extends Ask0Plugin {
  constructor(tracker) {
    super('analytics');
    this.tracker = tracker;
  }

  onOpen() {
    this.tracker.track('Widget Opened');
  }

  onMessage(message) {
    this.tracker.track('Message Sent', {
      type: message.isUser ? 'user' : 'assistant',
      length: message.content.length
    });
  }
}

// Register plugins
window.ask0.registerPlugin(new AnalyticsPlugin(analytics));

Available Plugins

Community plugins:

// Emoji picker plugin
import EmojiPlugin from '@ask0/plugin-emoji';

// Voice input plugin
import VoicePlugin from '@ask0/plugin-voice';

// Translation plugin
import TranslatePlugin from '@ask0/plugin-translate';

// Code highlighting plugin
import CodePlugin from '@ask0/plugin-code';

// Register multiple plugins
window.ask0.use([
  new EmojiPlugin(),
  new VoicePlugin({ lang: 'en-US' }),
  new TranslatePlugin({ target: 'auto' }),
  new CodePlugin({ theme: 'github-dark' })
]);

Performance Optimization

Lazy Loading

Load components on demand:

// Lazy load configuration
const lazyConfig = {
  // Load widget on first interaction
  widget: {
    trigger: 'interaction', // 'immediate', 'interaction', 'idle'
    preload: true
  },

  // Load features as needed
  features: {
    emoji: 'onDemand',
    voice: 'onDemand',
    fileUpload: 'onDemand',
    codeHighlight: 'onDemand'
  },

  // Resource loading
  resources: {
    images: 'lazy',
    fonts: 'swap',
    styles: 'async'
  }
};

// Apply lazy loading
window.ask0.lazyLoad = lazyConfig;

Caching Strategy

Optimize with caching:

// Cache configuration
const cacheConfig = {
  // Message cache
  messages: {
    enabled: true,
    maxSize: 100,
    ttl: 3600 // seconds
  },

  // Response cache
  responses: {
    enabled: true,
    maxSize: 50,
    ttl: 1800
  },

  // Asset cache
  assets: {
    enabled: true,
    strategies: {
      images: 'cacheFirst',
      scripts: 'networkFirst',
      styles: 'staleWhileRevalidate'
    }
  },

  // Storage
  storage: 'localStorage', // 'localStorage', 'sessionStorage', 'indexedDB'
  compress: true
};

Best Practices

Customization Best Practices:

  1. Keep customizations modular and maintainable
  2. Test across different browsers and devices
  3. Use CSS variables for consistent theming
  4. Optimize for performance
  5. Ensure accessibility isn't compromised
  6. Document custom configurations
  7. Version control your customizations

Next Steps