Skip to content

Quick Start

Start with style(), useCss(), css prop composition, and reusable style files.

Fluentic Style starts small. Write styles with style(), resolve them with useCss(), and pass the resolved result to the css prop.

/** @jsxImportSource @fluentic/style */
import { style, useCss } from '@fluentic/style';
const styles = {
button: style({
display: 'inline-flex',
alignItems: 'center',
border: 0,
borderRadius: 6,
padding: '8px 12px',
backgroundColor: '#2563eb',
color: 'white',
cursor: 'pointer',
}).hover({
backgroundColor: '#1d4ed8',
}),
};
export function Button() {
const css = useCss(styles);
return <button css={css.button}>Save</button>;
}

That is the core loop:

  • style(...) creates a typed style builder.
  • useCss(styles) resolves the builders for React.
  • css={css.button} puts the generated class on the element.

Keep related styles in one object, resolve that object once, and combine resolved items with arrays.

const styles = {
button: style({
display: 'inline-flex',
alignItems: 'center',
border: '1px solid rgb(15 23 42 / 0.14)',
borderRadius: 6,
padding: '8px 12px',
backgroundColor: 'white',
color: '#0f172a',
}),
primary: style({
borderColor: '#2563eb',
backgroundColor: '#2563eb',
color: 'white',
}),
fullWidth: style({
width: '100%',
}),
};
export function Button(props: { primary?: boolean; fullWidth?: boolean }) {
const css = useCss(styles);
return (
<button
css={[
css.button,
props.primary && css.primary,
props.fullWidth && css.fullWidth,
]}
>
Save
</button>
);
}

The css prop accepts items, arrays, nested arrays, and falsy values. This makes conditional styling feel like ordinary React composition.

Reusable components should let callers add element-level styles. Accept CssProp, then place it last in the css array so the caller can add layout or one-off adjustments.

/** @jsxImportSource @fluentic/style */
import { style, useCss } from '@fluentic/style';
import type { CssProp } from '@fluentic/style';
import type { ReactNode } from 'react';
const styles = {
button: style({
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
gap: 8,
border: '1px solid rgb(15 23 42 / 0.14)',
borderRadius: 6,
padding: '8px 12px',
backgroundColor: 'white',
color: '#0f172a',
}),
};
type ButtonProps = {
children: ReactNode;
css?: CssProp;
};
export function Button(props: ButtonProps) {
const css = useCss(styles);
return <button css={[css.button, props.css]}>{props.children}</button>;
}

Callers can pass another resolved style:

const pageStyles = {
wideAction: style({
minWidth: 160,
}),
};
export function Page() {
const css = useCss(pageStyles);
return <Button css={css.wideAction}>Publish</Button>;
}

Style builders are normal values. Define common styles once, export them, and combine them wherever you resolve CSS.

styles/common.ts
import { style } from '@fluentic/style';
export const focusRing = style().focusVisible({
outline: '2px solid #2563eb',
outlineOffset: 2,
});
export const inlineControl = style({
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
});
Button.styles.ts
import { style } from '@fluentic/style';
import { focusRing, inlineControl } from './styles/common';
export const buttonStyles = {
root: style({
border: '1px solid rgb(15 23 42 / 0.14)',
borderRadius: 6,
padding: '8px 12px',
backgroundColor: 'white',
color: '#0f172a',
})
.merge(inlineControl)
.merge(focusRing),
primary: style({
borderColor: '#2563eb',
backgroundColor: '#2563eb',
color: 'white',
}),
};
Button.tsx
/** @jsxImportSource @fluentic/style */
import { useCss } from '@fluentic/style';
import type { CssProp } from '@fluentic/style';
import type { ReactNode } from 'react';
import { buttonStyles } from './Button.styles';
type ButtonProps = {
children: ReactNode;
css?: CssProp;
primary?: boolean;
};
export function Button(props: ButtonProps) {
const css = useCss(buttonStyles);
return (
<button css={[css.root, props.primary && css.primary, props.css]}>
{props.children}
</button>
);
}

This is enough for many app components: define styles, reuse style builders, resolve once per component, and compose through css.

When a value becomes part of your design language, make it a token.

tokens.ts
import { createToken } from '@fluentic/style';
export const color = {
accent: createToken('#2563eb'),
accentText: createToken('#ffffff'),
};
Button.styles.ts
import { style } from '@fluentic/style';
import { color } from './tokens';
export const buttonStyles = {
root: style({
backgroundColor: color.accent,
color: color.accentText,
}),
};

Tokens keep a stable identity, so later themes can retarget the same design value without rewriting every component.

Use plain style() for styles that belong to one rendered element.

Use style.slot(...) when a component wants to publish a supported styling target, such as root, icon, label, or panel.

Use style.scope(...) when you want to encapsulate a group of overrides for a theme, variant, state, media query, or subtree.

Continue with Slots and Scopes when you are ready to design a public component styling API.