> ## Documentation Index
> Fetch the complete documentation index at: https://docs.firstflow.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Theming

> Re-theme the widget with CSS custom properties, override class hooks for finer control, or build your own shell.

The widget ships unopinionated styles you control with CSS. There are three levels, from a few variables to a fully custom shell.

## Import the styles

```tsx theme={null}
import "@firstflow/runtime/styles.css";
```

Without this import the widget renders unstyled. Import it once, near your app root.

## 1. Re-theme with CSS variables

Override the `--ff-*` custom properties on `:root` (or any ancestor of the widget):

```css theme={null}
:root {
  --ff-bg: #fef3c7;
  --ff-accent: #6366f1;
  --ff-radius-shell: 16px;
  --ff-widget-max-width: 420px;
  /* ~12 tokens total see packages/sdk-frontend/styles/default.css */
}
```

Dark mode toggles with a data attribute:

```html theme={null}
<html data-theme="dark">
```

## 2. Override class hooks

Every part of the widget has a stable class name. Target them in your own CSS for finer control:

`ff-widget`, `ff-widget__header`, `ff-block`, `ff-block__title`, `ff-block__body`, `ff-cta`, `ff-quick-reply`, `ff-option`, `ff-scale__dot`, and more.

```css theme={null}
.ff-cta { text-transform: uppercase; letter-spacing: 0.04em; }
.ff-quick-reply { border-radius: 999px; }
```

## 3. Build your own shell

For complete control over markup, skip `FirstflowWidget` and render `BlockRenderer` inside your own component, driven by [`useFirstflow`](/react/use-firstflow):

```tsx theme={null}
import { useFirstflow, findNode, BlockRenderer } from "@firstflow/runtime";

function MyWidget() {
  const { active, dismiss } = useFirstflow();
  if (!active) return null;
  const node = findNode(active.experience.flow, active.currentNodeId);
  if (!node) return null;
  return (
    <MyPanel onClose={dismiss}>
      <BlockRenderer node={node} />
    </MyPanel>
  );
}
```

The runtime state, triggers, navigation, and persistence keep working you only replace the shell. See [Blocks & custom shells](/widget-kit/overview).
