AI Sections¶
AI Sections is an embeddable component that displays predefined questions visitors can click to instantly trigger conversations with the Rose widget. It helps guide visitors toward relevant topics and increases engagement.
Overview¶
AI Sections provides a visual prompt section that:
- Displays contextual questions relevant to the page content
- Triggers the Rose widget when questions are clicked
- Animates smoothly with staggered entrance and shimmer effects
- Supports custom styling via props or CSS
- Integrates with unified appearance config for consistent branding
Architecture¶
Component Locations¶
| Component | Location | Purpose |
|---|---|---|
| AISections | frontend/shared/src/components/AISections/ |
Main container component |
| AISectionsButton | frontend/shared/src/components/AISections/ |
Individual question button |
| RoseSectionsManager | frontend/widget/src/ai-sections/ |
Widget-integrated manager |
Single Bundle Architecture
AI Sections is integrated into the main widget bundle (inboundx-widget.js). There is no separate sections bundle - the widget auto-discovers and renders sections.
Configuration¶
Database Configuration (Supabase)¶
AI Sections can be configured per-domain in the config.ai_sections table:
| Column | Type | Description |
|---|---|---|
section_id |
string | Unique identifier for the section |
site_domain |
string | Site domain this section belongs to |
title |
jsonb | Localized title object (e.g., {"en": "Ask us about pricing", "fr": "..."}) |
questions |
jsonb | Localized questions object (e.g., {"en": ["Q1", "Q2"], "fr": ["Q1 fr", "Q2 fr"]}) |
styling |
jsonb | Custom styling options |
custom_css |
text | Raw CSS for advanced theming |
enabled |
boolean | Whether the section is active |
hides_main_bar |
boolean | When true, hides the main Rose widget search bar while this section's search bar is visible on screen |
Inline Configuration¶
Sections can also be configured inline when embedding:
<AISections
config={{
title: 'Ask us about pricing',
questions: [
'What plan fits my needs?',
'Is there a free trial?',
'How does billing work?',
],
styling: {
primaryColor: '#0A42C3',
buttonBackground: '#F0F4FF',
},
hidesMainBar: true, // Hides main widget search bar when this section's search bar is visible
}}
/>
Localization Support
Both title and questions support localized content with language keys (en, fr, de, es, it).
The widget automatically selects the appropriate language based on visitor context, falling back to en.
Database format:
Styling¶
Style Resolution Architecture¶
AI Sections uses a three-layer styling architecture. Each property is resolved independently through this cascade:
Priority (highest to lowest):
- Explicit styling props → Inline styles, always wins
- Custom CSS → Injected stylesheet with
.ai-sections-*selectors - Appearance config color → From
appearance.brand_color(same as search bar) - Hardcoded defaults → Built-in fallback values
How It Works¶
Per-Property Resolution: Each styling property is resolved independently. For example:
// Config with partial styling
config={{
styling: {
buttonBackground: '#FF0000', // Explicit → Layer 1 wins
// primaryColor not set → Layers 2 or 3 apply
},
customCSS: `
.ai-sections-button {
--ai-sections-sparkle-color: #00FF00; /* Layer 2 for sparkle */
}
`
}}
In this example:
- buttonBackground → Uses #FF0000 (Layer 1 - explicit prop)
- primaryColor → Uses CSS variable if set, otherwise the resolved appearance color (Layers 2/3)
Layer 3 Default Chain:
The sparkle icon color defaults to the site's resolved appearance color (the same color used in the search bar), ensuring brand consistency without explicit configuration.
Styling Options¶
| Property | Type | Default | Description |
|---|---|---|---|
primaryColor |
string | Appearance config color | Icon color (sparkle) |
backgroundColor |
string | #E8F5F5 |
Section container background |
buttonBackground |
string | #FFFFFF |
Button background color |
buttonTextColor |
string | #1a1a1a |
Button text color |
buttonFontSize |
string | 20px |
Button text size |
buttonFontWeight |
string | 400 |
Button text weight |
iconSize |
number | 20 |
Sparkle icon size (px) |
arrowSize |
number | 24 |
Arrow icon size (px) |
titleColor |
string | #003E3F |
Section title color |
gap |
string | 12px |
Gap between buttons |
layout |
string | stacked |
stacked or inline |
searchBarPosition |
string | bottom |
Position of search bar: top or bottom |
unhideParent |
boolean | false |
Auto-show/hide the first hidden parent when section expands/collapses |
Custom CSS¶
For advanced theming, use the customCSS property with CSS targeting these classes:
/* Container */
.ai-sections-container {
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
}
/* Title */
.ai-sections-title {
color: #e0fc63;
font-size: 40px;
}
/* Buttons */
.ai-sections-button {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
}
/* Button text */
.ai-sections-button-text {
color: #ffffff;
}
/* Sparkle icon color override */
.ai-sections-button {
--ai-sections-sparkle-color: #e0fc63;
}
/* Arrow icon */
.ai-sections-arrow {
opacity: 0.8;
}
CSS Variables¶
| Variable | Description |
|---|---|
--ai-sections-bg |
Container background |
--ai-sections-title-color |
Title text color |
--ai-sections-primary-color |
Primary/accent color |
--ai-sections-button-bg |
Button background |
--ai-sections-button-text |
Button text color |
--ai-sections-gap |
Gap between buttons |
--ai-sections-sparkle-color |
Override sparkle icon color |
Integration¶
Auto-Discovery (Recommended)¶
AI Sections is built into the main widget bundle. Just load the widget and add containers with data-rose-section attributes:
<!-- Load the widget (includes AI Sections) -->
<script src="https://cdn.userose.ai/widget/{DOMAIN}/inboundx-widget.js"></script>
<!-- Add sections anywhere on the page - widget auto-discovers them -->
<div data-rose-section="homepage-hero"></div>
<div data-rose-section="pricing-faq"></div>
Conventional Container¶
Alternatively, use the conventional container ID for auto-loading all sections:
<!-- Widget auto-loads all enabled sections for the domain -->
<div id="rose-ai-sections-container"></div>
<script src="https://cdn.userose.ai/widget/{DOMAIN}/inboundx-widget.js"></script>
Global Config (Alternative)¶
For more control, define configuration before the widget loads:
<div id="my-section"></div>
<script>
window.RoseSectionsConfig = {
sections: [{
containerId: 'my-section',
title: 'How can we help?',
questions: ['What is your product?', 'How much does it cost?']
}]
};
</script>
<script src="https://cdn.userose.ai/widget/{DOMAIN}/inboundx-widget.js"></script>
Manual API (Advanced)¶
For dynamic rendering after page load:
// Render a section manually
window.RoseSections.renderSection({
containerId: 'my-container',
sectionId: 'pricing-faq', // or provide config directly
});
// Destroy a section
window.RoseSections.destroySection('my-container');
React Integration¶
For React applications within the Rose ecosystem:
import { AISections } from '@inboundx/shared';
function PricingPage() {
return (
<AISections
config={{
title: 'Pricing Questions',
questions: ['What plan fits my needs?', 'Is there a free trial?'],
}}
onQuestionClick={(question) => console.log('Clicked:', question)}
/>
);
}
Props Reference¶
AISections Props¶
| Prop | Type | Default | Description |
|---|---|---|---|
sectionId |
string | - | Database section ID to fetch config |
config |
AISectionConfig | - | Inline configuration object |
showShimmer |
boolean | true |
Show shimmer effect on first reveal |
staggerDelay |
number | 100 |
Delay (ms) between button animations |
collapseTimeout |
number | 10000 |
Timeout before collapsing if widget fails |
onQuestionClick |
function | - | Callback when question is clicked |
onExpand |
function | - | Callback when section expands |
onCollapse |
function | - | Callback when section collapses |
className |
string | - | Additional CSS class for container |
AISectionConfig Options¶
| Option | Type | Default | Description |
|---|---|---|---|
title |
string | - | Section title displayed above questions |
questions |
string[] | - | Array of question strings to display as buttons |
styling |
object | - | Styling options (see Styling Options) |
customCSS |
string | - | Raw CSS for advanced theming |
hidesMainBar |
boolean | false |
When true, hides the main Rose widget search bar while this section's search bar is visible on screen |
disableOnMobile |
boolean | false |
When true, hides the entire section on mobile devices |
Behavior¶
Widget Readiness¶
AI Sections automatically detects when the Rose widget is ready:
- Section starts collapsed (hidden)
- Waits for widget to emit
rose:readyevent - Expands with animation when widget is available
- Collapses if widget fails to load within timeout
Question Click Flow¶
- User clicks a question button
triggerQuestion()is called on the widget- Widget opens and sends the question
onQuestionClickcallback fires (if provided)
Unhide Parent¶
When the widget bar is hidden (traffic control, URL exclusion, etc.), AI sections collapse. But the host page container wrapping the section element may remain visible, leaving empty space.
The unhideParent styling option solves this by automatically toggling the first hidden parent:
- On mount, traverses up to 10 parent elements to find the first one that is hidden
- Detects the hiding method(s) used:
display: none,visibility: hidden,opacity: 0,clip-path,height: 0,hiddenattribute, oraria-hidden="true" - When the section expands (widget bar visible) → unhides the parent by reversing each detected method
- When the section collapses (widget bar hidden) → re-hides the parent using the original methods
- On destroy → restores the parent to its original hidden state
<!-- Client wraps the section in a hidden container -->
<div style="display: none">
<div data-rose-section="faq"></div>
</div>
With unhideParent: true in the section's styling config, the hidden wrapper is automatically shown/hidden in sync with the widget bar.
JS works even when parent is hidden
The React root mounts and runs regardless of display: none. The useWidgetReadiness hook listens for window events (inboundx_bar_visible / inboundx_bar_hidden), not DOM visibility.
Animation¶
- Entrance: Buttons fade in and slide up with staggered delay
- Shimmer: One-time shimmer effect on first reveal
- Exit: Smooth collapse if widget becomes unavailable
Main Bar Visibility¶
When hides_main_bar is enabled for a section, the main Rose widget search bar will automatically hide when the AI section's search bar scrolls into view:
- Uses
IntersectionObserverto detect when the section's search bar is ≥20% visible - Dispatches
inboundx_section_searchbar_enteredevent when visible - Dispatches
inboundx_section_searchbar_exitedevent when hidden - The main widget animates smoothly out of view to avoid duplicate search bars on screen
This prevents visual clutter when a page has both an embedded AI section search bar and the main floating widget search bar.
Preview Mode¶
The widget exposes preview() and exitPreview() methods on window.RoseSections to allow staff to preview disabled sections on the live site without enabling them for visitors.
Public API¶
// Show all sections including disabled ones
await window.RoseSections.preview()
// Revert to showing only enabled sections
await window.RoseSections.exitPreview()
How It Works¶
preview()setspreviewMode = trueon theRoseSectionsManagersingleton- Destroys all currently rendered sections
- Re-runs auto-discovery, passing
{ includeDisabled: true }togetAISection()andgetAISectionsForDomain() - The service layer skips the
.eq('enabled', true)filter whenincludeDisabledis set exitPreview()reverses the process — setspreviewMode = falseand re-renders with the default enabled-only filter
RLS Policy¶
The anon RLS policy on config.ai_sections allows reading all rows (including disabled). The enabled filter is enforced at the application layer in the JS service functions, not at the database level. This allows the preview mode to bypass the filter without needing authenticated access.
Testing¶
Use the preprod-ui to test AI Sections configurations:
- Navigate to
/ai-sectionsin preprod-ui - Select a client from the dropdown
- Toggle between database and sample configs
- Test different layout modes (stacked/inline)
- View the current configuration JSON
- Use the eye toggle button to hide/show the widget bar (tests
unhideParentbehavior) - Select the "unhide-parent" sample config to see the hidden-parent demo
Analytics¶
AI Sections interactions are tracked via PostHog:
| Event | Description |
|---|---|
rw_ai_section_impression |
Section displayed to user |
rw_ai_section_question_click |
Question button clicked |
Related Documentation¶
- Website Agent - Parent agent overview
- Dynamic Questions - Per-page question configuration
- Preprod Testing - Testing interface guide