Skip to content
TypeScript-first · React · Atomic CSS

Fluentic
Style

Typed styling contracts for React component systems — fluent chains for authors, slots for public override points, scopes for themes, and atomic CSS for delivery.

$ npm install @fluentic/style
button.styles.ts
import { style, token } from '@fluentic/style';

const button = {
  root: style.slot({
    display: 'inline-flex',
    borderRadius: 8,
    padding: '8px 12px',
  }).hover({ opacity: 0.88 }),
  label: style.slot({ fontWeight: 650 }),
};

// Compose a scope for variants & themes
const primary = style.scope([
  button.root({ backgroundColor: token.accent }),
  button.label({ fontWeight: 760 }),
]);

// Use it in your component
export function Button({ scope }) {
  const css = useCss(button, scope);
  return <button css={css.root} scope={css}>…</button>;
}
⌨️

Typed Contracts

Slots are TypeScript types. Every overridable part of a component has a name your IDE can check, autocomplete, and break-detect on rename.

Atomic CSS

One declaration = one class. Zero duplication, zero specificity wars. Order is deterministic whether you use the runtime or extract to CSS.

🔗

Fluent Chains

.hover(), .focus(), .media(), .container() — selector variants chain directly on any slot, keeping logic close to the component that owns it.

📦

Static Extraction

Static chains compile to atomic CSS at build time. Switch from runtime to extracted output without touching a single line of source code.

Why Fluentic Style?

Styling a component library becomes messy when base styles, consumer overrides, theme layers, and build performance all pull in different directions. Fluentic Style separates the concerns so each one stays manageable.

🎯

Styling as a public API

With CSS Modules or plain class names, consumers override by guessing names that can change any release. Slots are a contract: named, typed, and stable across versions — like a prop surface for styles.

🏗️

No specificity wars

Emotion, styled-components, and similar tools accumulate specificity as theme layers stack. Atomic CSS gives each property exactly one class; cascade order is explicit and predictable regardless of how many scopes are active.

🚀

Runtime first, compile when ready

Start in runtime mode — it works in Vitest, Storybook, SSR, and any framework. When you ship to production, extract the static subset to CSS files without changing the source and without running a separate codegen step.

🧩

Scoped themes, no Provider hell

Scopes carry overrides through a subtree via the scope prop. No wrapper components, no CSS custom property gymnastics, no generated class names leaking into consumer DOM.

How it compares

Most tools solve one problem well. Fluentic Style connects them into a single coherent model without asking you to give up TypeScript, atomic CSS, or runtime flexibility.

Capability Fluentic Style CSS Modules CSS-in-JS Tailwind
Typed override targets (slots)
Atomic CSS output varies
Runtime-first (no build step)
Static CSS extraction limited
Subtree theme scoping varies
TypeScript-first authoring partial partial
Predictable cascade order manual

Zero config to start

Install the package and write your first slot — runtime mode works immediately, no build plugin needed. Add Vite or webpack extraction when you're ready for static CSS output, without touching a line of source.