or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

compatibility.mdcore-tss-api.mdcss-utilities.mddsfr-integration.mdglobal-styles-keyframes.mdindex.mdmakestyles-api.mdmui-integration.mdnextjs-ssr.mdwithstyles-hoc.md

css-utilities.mddocs/

0

# CSS Utilities

1

2

TSS-React provides low-level CSS generation and class manipulation utilities for advanced use cases, direct Emotion integration, and fine-grained styling control. These utilities form the foundation of the higher-level APIs.

3

4

## Capabilities

5

6

### CSS and CX Factory Function

7

8

Creates css and cx functions bound to a specific Emotion cache instance.

9

10

```typescript { .api }

11

/**

12

* Creates css and cx functions for a given Emotion cache

13

* @param params - Configuration object with cache instance

14

* @returns Object containing css and cx functions

15

*/

16

function createCssAndCx(params: { cache: EmotionCache }): {

17

css: Css;

18

cx: Cx;

19

};

20

21

interface EmotionCache {

22

key: string;

23

inserted: Record<string, string | boolean>;

24

registered: Record<string, string>;

25

sheet: StyleSheet;

26

compat?: boolean;

27

nonce?: string;

28

insert: (rule: string, serialized: SerializedStyles) => void;

29

}

30

```

31

32

**Usage Examples:**

33

34

```typescript

35

import createCache from "@emotion/cache";

36

import { createCssAndCx } from "tss-react/cssAndCx";

37

38

// Create custom cache

39

const cache = createCache({

40

key: "my-styles",

41

prepend: true

42

});

43

44

// Create css and cx functions

45

const { css, cx } = createCssAndCx({ cache });

46

47

// Use css function for dynamic styles

48

const dynamicClass = css({

49

backgroundColor: "blue",

50

color: "white",

51

padding: 16,

52

"&:hover": {

53

backgroundColor: "darkblue"

54

}

55

});

56

57

// Use cx function for combining classes

58

const combinedClass = cx(

59

"base-class",

60

dynamicClass,

61

{

62

"conditional-class": true,

63

"disabled-class": false

64

}

65

);

66

67

function MyComponent() {

68

return <div className={combinedClass}>Styled content</div>;

69

}

70

```

71

72

### CSS-and-CX Hook Factory

73

74

Creates a React hook that returns css and cx functions with cache integration.

75

76

```typescript { .api }

77

/**

78

* Creates a hook that returns css and cx functions

79

* @param params - Configuration object with cache hook

80

* @returns Object containing useCssAndCx hook

81

*/

82

function createUseCssAndCx(params: {

83

useCache: () => EmotionCache;

84

}): {

85

useCssAndCx(): { css: Css; cx: Cx };

86

};

87

```

88

89

**Usage Examples:**

90

91

```typescript

92

import createCache from "@emotion/cache";

93

import { createUseCssAndCx } from "tss-react/cssAndCx";

94

import { useState } from "react";

95

96

// Create cache provider hook

97

function useMyCache() {

98

const [cache] = useState(() => createCache({

99

key: "dynamic-styles",

100

prepend: true

101

}));

102

return cache;

103

}

104

105

// Create hook factory

106

const { useCssAndCx } = createUseCssAndCx({

107

useCache: useMyCache

108

});

109

110

// Use in components

111

function DynamicStyledComponent({ variant }: { variant: "primary" | "secondary" }) {

112

const { css, cx } = useCssAndCx();

113

114

const baseStyles = css({

115

padding: "12px 24px",

116

borderRadius: "4px",

117

border: "none",

118

cursor: "pointer",

119

fontSize: "16px",

120

fontWeight: "500",

121

transition: "all 0.2s ease"

122

});

123

124

const variantStyles = css(

125

variant === "primary"

126

? {

127

backgroundColor: "#007bff",

128

color: "white",

129

"&:hover": { backgroundColor: "#0056b3" }

130

}

131

: {

132

backgroundColor: "#6c757d",

133

color: "white",

134

"&:hover": { backgroundColor: "#545b62" }

135

}

136

);

137

138

return (

139

<button className={cx(baseStyles, variantStyles)}>

140

Click me

141

</button>

142

);

143

}

144

```

145

146

### CSS Function Interface

147

148

Template literal and function-based CSS generation with Emotion serialization.

149

150

```typescript { .api }

151

interface Css {

152

/**

153

* Template literal CSS generation

154

* @param template - Template strings array

155

* @param args - Interpolated CSS values

156

* @returns Generated CSS class name

157

*/

158

(template: TemplateStringsArray, ...args: CSSInterpolation[]): string;

159

160

/**

161

* Function-based CSS generation

162

* @param args - CSS interpolation values

163

* @returns Generated CSS class name

164

*/

165

(...args: CSSInterpolation[]): string;

166

}

167

168

type CSSInterpolation =

169

| CSSObject

170

| string

171

| number

172

| false

173

| null

174

| undefined

175

| CSSInterpolation[];

176

```

177

178

**Usage Examples:**

179

180

```typescript

181

import { css } from "my-css-instance";

182

183

// Template literal usage

184

const templateClass = css`

185

background-color: ${"red"};

186

color: white;

187

padding: ${16}px;

188

189

&:hover {

190

background-color: ${"darkred"};

191

}

192

193

@media (max-width: 768px) {

194

padding: ${8}px;

195

}

196

`;

197

198

// Function usage with CSS object

199

const objectClass = css({

200

backgroundColor: "blue",

201

color: "white",

202

padding: 16,

203

borderRadius: 4,

204

"&:hover": {

205

backgroundColor: "darkblue",

206

transform: "scale(1.02)"

207

},

208

"@media (max-width: 768px)": {

209

padding: 8,

210

fontSize: 14

211

}

212

});

213

214

// Function usage with multiple arguments

215

const multiArgClass = css(

216

{ backgroundColor: "green", color: "white" },

217

{ padding: 12, borderRadius: 8 },

218

{

219

"&:active": {

220

transform: "scale(0.98)"

221

}

222

}

223

);

224

225

// Dynamic CSS generation

226

function createButtonStyles(theme: any, variant: string) {

227

return css({

228

backgroundColor: theme.colors[variant],

229

color: theme.textColors[variant],

230

padding: theme.spacing.md,

231

borderRadius: theme.borderRadius,

232

border: "none",

233

cursor: "pointer",

234

fontSize: theme.fontSizes.md,

235

fontWeight: theme.fontWeights.semibold,

236

transition: "all 0.2s ease",

237

"&:hover": {

238

backgroundColor: theme.colors[`${variant}Hover`],

239

boxShadow: theme.shadows.md

240

},

241

"&:focus": {

242

outline: `2px solid ${theme.colors.focus}`,

243

outlineOffset: "2px"

244

},

245

"&:disabled": {

246

opacity: 0.5,

247

cursor: "not-allowed"

248

}

249

});

250

}

251

```

252

253

### CX Function Interface

254

255

Classname combination utility with conditional logic support.

256

257

```typescript { .api }

258

/**

259

* Combines multiple class names with conditional logic

260

* @param classNames - Array of class name arguments

261

* @returns Combined class name string

262

*/

263

type Cx = (...classNames: CxArg[]) => string;

264

265

type CxArg =

266

| string

267

| number

268

| boolean

269

| undefined

270

| null

271

| { [className: string]: boolean | undefined | null }

272

| CxArg[];

273

```

274

275

**Usage Examples:**

276

277

```typescript

278

import { cx } from "my-cx-instance";

279

280

// Basic class combination

281

const basicClasses = cx("btn", "btn-primary", "btn-large");

282

// Result: "btn btn-primary btn-large"

283

284

// Conditional classes with object syntax

285

const conditionalClasses = cx(

286

"btn",

287

{

288

"btn-primary": variant === "primary",

289

"btn-secondary": variant === "secondary",

290

"btn-disabled": disabled,

291

"btn-loading": loading

292

}

293

);

294

295

// Mixed arguments

296

const mixedClasses = cx(

297

"base-class",

298

dynamicClass,

299

{

300

"active": isActive,

301

"highlighted": isHighlighted

302

},

303

extraClass && "extra-class",

304

["array", "of", "classes"]

305

);

306

307

// Real-world component example

308

function Button({

309

variant = "primary",

310

size = "medium",

311

disabled = false,

312

loading = false,

313

className,

314

children,

315

...props

316

}: ButtonProps) {

317

const { css, cx } = useCssAndCx();

318

319

const baseStyles = css({

320

border: "none",

321

borderRadius: 4,

322

cursor: "pointer",

323

fontWeight: 500,

324

transition: "all 0.2s ease",

325

display: "inline-flex",

326

alignItems: "center",

327

justifyContent: "center"

328

});

329

330

const sizeStyles = css({

331

padding: {

332

small: "6px 12px",

333

medium: "8px 16px",

334

large: "12px 24px"

335

}[size],

336

fontSize: {

337

small: "14px",

338

medium: "16px",

339

large: "18px"

340

}[size]

341

});

342

343

const variantStyles = css(

344

variant === "primary"

345

? { backgroundColor: "#007bff", color: "white" }

346

: { backgroundColor: "#6c757d", color: "white" }

347

);

348

349

const buttonClass = cx(

350

baseStyles,

351

sizeStyles,

352

variantStyles,

353

{

354

[css({ opacity: 0.5, cursor: "not-allowed" })]: disabled,

355

[css({ position: "relative", "&::after": { content: '"..."' } })]: loading

356

},

357

className

358

);

359

360

return (

361

<button

362

className={buttonClass}

363

disabled={disabled || loading}

364

{...props}

365

>

366

{children}

367

</button>

368

);

369

}

370

```

371

372

### Class Merging Utility

373

374

Utility function for merging style classes with override support, particularly useful for component libraries.

375

376

```typescript { .api }

377

/**

378

* Merges classes from useStyles with override classes

379

* @param classesFromUseStyles - Classes generated by TSS-React hooks

380

* @param classesOverrides - Optional override classes from props

381

* @param cx - Class combination function

382

* @returns Merged classes object with overrides applied

383

*/

384

function mergeClasses<T extends string, U extends string>(

385

classesFromUseStyles: Record<T, string>,

386

classesOverrides: Partial<Record<U, string>> | undefined,

387

cx: Cx

388

): Record<T, string> & Partial<Record<Exclude<U, T>, string>>;

389

```

390

391

**Usage Examples:**

392

393

```typescript

394

import { mergeClasses } from "tss-react";

395

import { tss } from "tss-react";

396

397

// Component with style overrides support

398

interface CardProps {

399

title: string;

400

children: React.ReactNode;

401

classes?: {

402

root?: string;

403

title?: string;

404

content?: string;

405

footer?: string;

406

};

407

}

408

409

const useCardStyles = tss.create({

410

root: {

411

backgroundColor: "white",

412

borderRadius: 8,

413

padding: 16,

414

boxShadow: "0 2px 4px rgba(0,0,0,0.1)"

415

},

416

title: {

417

fontSize: 18,

418

fontWeight: "bold",

419

marginBottom: 12,

420

color: "#333"

421

},

422

content: {

423

color: "#666",

424

lineHeight: 1.5

425

}

426

});

427

428

function Card({ title, children, classes: classesOverrides }: CardProps) {

429

const { classes: defaultClasses, cx } = useCardStyles();

430

431

// Merge default classes with overrides

432

const classes = mergeClasses(defaultClasses, classesOverrides, cx);

433

434

return (

435

<div className={classes.root}>

436

<h3 className={classes.title}>{title}</h3>

437

<div className={classes.content}>{children}</div>

438

</div>

439

);

440

}

441

442

// Usage with overrides

443

function App() {

444

return (

445

<Card

446

title="Custom Card"

447

classes={{

448

root: "custom-card-root",

449

title: "custom-card-title",

450

// Additional classes not in original component

451

footer: "custom-footer-class"

452

}}

453

>

454

<p>Card content here</p>

455

</Card>

456

);

457

}

458

459

// Component library pattern

460

const useLibraryStyles = tss

461

.withParams<{ variant: "outlined" | "filled"; size: "small" | "large" }>()

462

.create(({ theme }, { variant, size }) => ({

463

root: {

464

padding: size === "small" ? 8 : 16,

465

borderRadius: 4,

466

...(variant === "outlined" && {

467

border: "1px solid #ccc",

468

backgroundColor: "transparent"

469

}),

470

...(variant === "filled" && {

471

backgroundColor: "#f5f5f5",

472

border: "none"

473

})

474

},

475

label: {

476

fontSize: size === "small" ? 14 : 16,

477

fontWeight: 500

478

}

479

}));

480

481

interface LibraryComponentProps {

482

variant: "outlined" | "filled";

483

size: "small" | "large";

484

label: string;

485

classes?: {

486

root?: string;

487

label?: string;

488

icon?: string;

489

};

490

}

491

492

function LibraryComponent({

493

variant,

494

size,

495

label,

496

classes: classesOverrides

497

}: LibraryComponentProps) {

498

const { classes: defaultClasses, cx } = useLibraryStyles({ variant, size });

499

const classes = mergeClasses(defaultClasses, classesOverrides, cx);

500

501

return (

502

<div className={classes.root}>

503

<span className={classes.label}>{label}</span>

504

{classes.icon && <span className={classes.icon}>→</span>}

505

</div>

506

);

507

}

508

```

509

510

### Advanced Patterns

511

512

#### Custom Cache Strategies

513

514

```typescript

515

import createCache from "@emotion/cache";

516

import { createCssAndCx } from "tss-react/cssAndCx";

517

518

// Performance-optimized cache

519

const performanceCache = createCache({

520

key: "perf",

521

speedy: true, // No text content in style elements (faster)

522

prepend: true // Insert at beginning of head

523

});

524

525

// Development cache with debugging

526

const devCache = createCache({

527

key: "dev",

528

speedy: false, // Include text content for debugging

529

nonce: process.env.CSP_NONCE

530

});

531

532

const { css: perfCss, cx: perfCx } = createCssAndCx({

533

cache: process.env.NODE_ENV === "production" ? performanceCache : devCache

534

});

535

```

536

537

#### Dynamic Style Generation

538

539

```typescript

540

function createDynamicStyles(theme: any) {

541

const { css, cx } = createCssAndCx({ cache: theme.cache });

542

543

return {

544

// Generate styles based on theme configuration

545

generateButtonStyle: (variant: string, size: string) => css({

546

backgroundColor: theme.palette[variant].main,

547

color: theme.palette[variant].contrastText,

548

padding: theme.spacing[size],

549

borderRadius: theme.shape.borderRadius,

550

fontSize: theme.typography[size].fontSize,

551

"&:hover": {

552

backgroundColor: theme.palette[variant].dark

553

}

554

}),

555

556

// Combine multiple dynamic styles

557

combineStyles: (...styles: string[]) => cx(...styles),

558

559

// Generate responsive utilities

560

generateResponsiveStyle: (breakpoints: Record<string, any>) => css(

561

Object.entries(breakpoints).reduce((acc, [bp, styles]) => ({

562

...acc,

563

[`@media (min-width: ${theme.breakpoints[bp]})`]: styles

564

}), {})

565

)

566

};

567

}

568

```