Skip to main content

Styling

The Fanfare SDK provides multiple approaches to customize widget appearance, from CSS variables to scoped styles and container classes.

CSS Custom Properties

All widgets use CSS custom properties for styling, making them easy to customize:
:root {
  /* Colors */
  --fanfare-primary: #3b82f6;
  --fanfare-primary-hover: #2563eb;
  --fanfare-primary-foreground: #ffffff;
  --fanfare-background: #ffffff;
  --fanfare-foreground: #1f2937;
  --fanfare-muted: #f3f4f6;
  --fanfare-muted-foreground: #6b7280;
  --fanfare-border: #e5e7eb;
  --fanfare-destructive: #ef4444;
  --fanfare-success: #22c55e;
  --fanfare-warning: #f59e0b;

  /* Typography */
  --fanfare-font-family: system-ui, -apple-system, sans-serif;
  --fanfare-font-size-xs: 0.75rem;
  --fanfare-font-size-sm: 0.875rem;
  --fanfare-font-size-base: 1rem;
  --fanfare-font-size-lg: 1.125rem;
  --fanfare-font-size-xl: 1.25rem;

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

  /* Border Radius */
  --fanfare-radius: 0.5rem;
  --fanfare-radius-sm: 0.25rem;
  --fanfare-radius-lg: 0.75rem;
  --fanfare-radius-full: 9999px;

  /* Shadows */
  --fanfare-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
  --fanfare-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
  --fanfare-shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
}

Scoped Widget Styling

Target specific widgets using their tag names:
/* Style only the queue widget */
fanfare-queue-widget {
  --fanfare-primary: #8b5cf6;
  --fanfare-radius: 1rem;
}

/* Style only the auction widget */
fanfare-auction-widget {
  --fanfare-primary: #ef4444;
  --fanfare-radius: 0;
}

/* Style only the draw widget */
fanfare-draw-widget {
  --fanfare-primary: #10b981;
  --fanfare-radius: 0.75rem;
}

Container Classes

Add custom classes to widget containers:
<fanfare-queue-widget queue-id="queue_123" container-class="my-custom-widget premium-style" />
.my-custom-widget {
  max-width: 400px;
  margin: 0 auto;
  padding: 1rem;
}

.premium-style {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border-radius: 1rem;
  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
}

Shadow DOM Considerations

Widgets are built with SolidJS and may use Shadow DOM encapsulation. To ensure your styles apply: CSS variables pierce Shadow DOM boundaries:
/* This will work even with Shadow DOM */
fanfare-queue-widget {
  --fanfare-primary: #8b5cf6;
}

Using ::part() Selectors

Widgets expose parts for styling:
fanfare-queue-widget::part(card) {
  border: 2px solid var(--fanfare-primary);
}

fanfare-queue-widget::part(button) {
  font-weight: 700;
  text-transform: uppercase;
}

fanfare-queue-widget::part(title) {
  font-size: 1.5rem;
}

Layout Integration

Responsive Container

function ResponsiveWidget() {
  return (
    <div className="widget-container">
      <fanfare-queue-widget queue-id="queue_123" />
    </div>
  );
}
.widget-container {
  width: 100%;
  max-width: 480px;
  margin: 0 auto;
  padding: 1rem;
}

@media (max-width: 640px) {
  .widget-container {
    padding: 0.5rem;
  }
}

Grid Layout

function WidgetGrid() {
  return (
    <div className="widget-grid">
      <fanfare-queue-widget queue-id="queue_1" />
      <fanfare-draw-widget draw-id="draw_1" />
      <fanfare-auction-widget auction-id="auction_1" />
    </div>
  );
}
.widget-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 1.5rem;
  padding: 1rem;
}

Animation Customization

Transition Timing

fanfare-experience-widget {
  --fanfare-transition-duration: 200ms;
  --fanfare-transition-timing: ease-in-out;
}

Loading Animation

/* Custom loading spinner */
fanfare-queue-widget::part(spinner) {
  border-color: var(--fanfare-primary);
  border-top-color: transparent;
  animation: spin 0.8s linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

State Transitions

/* Smooth state changes */
fanfare-queue-widget::part(content) {
  transition:
    opacity 200ms ease,
    transform 200ms ease;
}

fanfare-queue-widget[data-status="loading"]::part(content) {
  opacity: 0.5;
  transform: scale(0.98);
}

Brand Examples

Blue Theme

:root {
  --fanfare-primary: #3b82f6;
  --fanfare-primary-hover: #2563eb;
  --fanfare-background: #ffffff;
  --fanfare-foreground: #1e3a5f;
}

Purple Theme

:root {
  --fanfare-primary: #8b5cf6;
  --fanfare-primary-hover: #7c3aed;
  --fanfare-background: #faf5ff;
  --fanfare-foreground: #581c87;
}

Green Theme

:root {
  --fanfare-primary: #10b981;
  --fanfare-primary-hover: #059669;
  --fanfare-background: #ecfdf5;
  --fanfare-foreground: #064e3b;
}

Dark Theme

:root {
  --fanfare-primary: #60a5fa;
  --fanfare-primary-hover: #3b82f6;
  --fanfare-background: #1f2937;
  --fanfare-foreground: #f9fafb;
  --fanfare-muted: #374151;
  --fanfare-border: #4b5563;
}

Z-Index Management

Widgets respect your z-index stacking context:
/* Ensure widget modals appear above your content */
fanfare-experience-widget {
  --fanfare-z-modal: 1000;
  --fanfare-z-overlay: 999;
}
@media print {
  fanfare-queue-widget,
  fanfare-draw-widget,
  fanfare-auction-widget {
    display: none;
  }
}

/* Or show a static version */
@media print {
  fanfare-queue-widget::part(actions) {
    display: none;
  }

  fanfare-queue-widget::part(card) {
    box-shadow: none;
    border: 1px solid #000;
  }
}

Accessibility Considerations

Focus Styles

fanfare-queue-widget::part(button):focus-visible {
  outline: 2px solid var(--fanfare-primary);
  outline-offset: 2px;
}

High Contrast Mode

@media (prefers-contrast: high) {
  fanfare-queue-widget {
    --fanfare-border: #000000;
    --fanfare-foreground: #000000;
    --fanfare-background: #ffffff;
  }
}

Reduced Motion

@media (prefers-reduced-motion: reduce) {
  fanfare-experience-widget {
    --fanfare-transition-duration: 0ms;
  }

  fanfare-queue-widget::part(spinner) {
    animation: none;
  }
}