Utilities
cn(), useIsMobile(), and shared patterns.
A small set of helpers used by every Flow UI component.
cn()
Merges class names like clsx, but also de-duplicates Tailwind utilities that conflict. Use it anywhere you'd use clsx alone.
import { cn } from "@flow-industries/ui";
cn("p-4", condition && "bg-brand", className);
// → "p-4 bg-brand <whatever caller passed>"
cn("p-4 p-6");
// → "p-6" (later wins, clsx + tailwind-merge de-duped)Every Flow component calls cn() internally on its root element, so any className you pass composes predictably with the built-in classes.
useIsMobile()
Returns a boolean that flips below a standard breakpoint (default: 768px). Live-updates on resize.
import { useIsMobile } from "@flow-industries/ui";
function Nav() {
const isMobile = useIsMobile();
return isMobile ? <MobileNav /> : <DesktopNav />;
}The hook matches (max-width: 767px). If you need a different breakpoint, write your own with matchMedia — the Flow hook is just the common case.
Shared patterns
render prop (not asChild)
Flow UI is built on Base UI, so composition uses the render prop:
<Button render={<a href="/home" />}>Home</Button>Renders an <a> with button styles and behavior. Do not use asChild — that's Radix, not Base UI.
data-slot attributes
Every component tags its root with data-slot="<name>". Style or query across consumers without wrestling with generated class names:
[data-slot="button"] {
font-feature-settings: "tnum";
}Reduced motion
Motion-driven components — Accordion, Tabs, WordFlash, Dock, and the animated icons — call useReducedMotion() from motion/react to skip or shorten their JS-driven transitions when the user prefers reduced motion. Everything else is covered for free by a global @media (prefers-reduced-motion: reduce) reset in base.css that collapses all remaining CSS animations and transitions, so you never have to wire this up yourself.
No transition-all
Flow UI components specify exact transition properties — transition-colors, transition-[border-color,background-color], never transition-all. If you override styles, match this discipline to avoid accidentally animating padding, font-size, or layout.