Custom Themes
AlpineFlow separates layout from appearance, letting you build a fully custom theme on top of the structural CSS layer.
CSS architecture
AlpineFlow ships three CSS layers:
| File | Purpose | Required? |
|---|---|---|
structural.css |
Positioning, z-index, cursors, transforms, var() references |
Yes |
theme-default.css |
Neutral zinc/slate theme with light + dark mode | Optional |
theme-flux.css |
Tailwind v4 / Flux UI native theme using design tokens | Optional |
The barrel file alpineflow.css imports structural + default:
/* alpineflow.css */
@import './alpineflow/structural.css';
@import './alpineflow/theme-default.css';
Structural-only mode
Import just structural.css for positioning and interaction with zero visual opinions:
@import './alpineflow/structural.css';
/* Bring your own theme below */
All positioning, z-indexing, cursors, and transforms work. Every visual property reads from var(--flow-*) with minimal fallbacks (transparent backgrounds, no shadows, no borders). Set the variables you care about and leave the rest.
This is useful when embedding AlpineFlow into a design system with its own tokens.
<div x-data="flowCanvas({
nodes: [
{ id: 'a', position: { x: 20, y: 30 }, data: { label: 'Custom' } },
{ id: 'b', position: { x: 250, y: 30 }, data: { label: 'Theme' } },
],
edges: [
{ id: 'e1', source: 'a', target: 'b' },
],
background: 'dots',
controls: false,
pannable: false,
zoomable: false,
})" class="flow-container" style="height: 180px; --flow-node-bg: #1e1b4b; --flow-node-color: #e0e7ff; --flow-node-border-color: #4338ca; --flow-node-border-top: 3px solid #818cf8; --flow-bg-dot-color: #312e81; --flow-edge-stroke: #6366f1;">
<div x-flow-viewport>
<template x-for="node in nodes" :key="node.id">
<div x-flow-node="node">
<div x-flow-handle:target.left></div>
<span x-text="node.data.label"></span>
<div x-flow-handle:source.right></div>
</div>
</template>
</div>
</div>
Accent stripe
The default theme adds a distinctive colored top border to nodes via --flow-node-border-top:
/* Light mode: subtle zinc stripe */
--flow-node-border-top: 2.5px solid #d4d4d8;
/* Dark mode: slightly brighter */
--flow-node-border-top: 2.5px solid #52525b;
The structural CSS applies this with a fallback:
.flow-node {
border-top: var(--flow-node-border-top, var(--flow-node-border));
}
To disable the accent stripe and use the same border on all sides:
.flow-container {
--flow-node-border-top: var(--flow-node-border);
}
To use a brand-colored stripe:
.flow-container {
--flow-node-border-top: 2.5px solid #3b82f6;
}
Flux theme
The Flux theme maps all --flow-* variables to Tailwind v4 design tokens and Flux UI's --color-accent system. Flowcharts automatically match your app's look and adapt to any TweakFlux preset (Dracula, Nord, Catppuccin, etc.).
@import './alpineflow/structural.css';
@import './alpineflow/theme-flux.css';
Key mappings:
| AlpineFlow Variable | Flux Token |
|---|---|
--flow-node-hover-border-color |
var(--color-accent) |
--flow-node-selected-border-color |
var(--color-accent) |
--flow-edge-stroke-selected |
var(--color-accent) |
--flow-node-bg |
var(--color-white) / var(--color-zinc-800) |
--flow-node-border |
var(--color-zinc-200) / var(--color-zinc-700) |
--flow-node-border-radius |
var(--radius-lg) |
--flow-node-shadow |
var(--shadow-xs) |
--flow-selection-bg |
color-mix(in oklab, var(--color-accent) 6%, transparent) |
The Flux theme uses color-mix(in oklab, ...) for accent transparencies, matching Flux UI's convention.
Building a custom theme file
Create a new CSS file that sets --flow-* variables on .flow-container:
/* my-theme.css */
.flow-container {
--flow-bg-color: #1e1e2e;
--flow-node-bg: #313244;
--flow-node-color: #cdd6f4;
--flow-node-border: 1px solid #45475a;
--flow-node-border-radius: 8px;
--flow-node-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
--flow-edge-stroke: #6c7086;
--flow-handle-bg: #89b4fa;
/* ... set as many or as few variables as needed */
}
Import it after the structural layer:
@import './alpineflow/structural.css';
@import './my-theme.css';
Any variables you don't set will use the structural layer's minimal fallbacks.