or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.mdmiddleware.mdparser.mdserialization.mdtokenization.mdutilities.md

middleware.mddocs/

0

# Middleware System

1

2

Pluggable transformation pipeline that allows custom CSS processing, vendor prefixing, namespacing, and other AST manipulations during serialization.

3

4

## Capabilities

5

6

### Middleware Function

7

8

Combines multiple middleware functions into a single processing pipeline that executes in sequence.

9

10

```javascript { .api }

11

/**

12

* Combine multiple middleware functions into processing pipeline

13

* @param collection - Array of middleware functions to execute in sequence

14

* @returns Combined middleware function

15

*/

16

function middleware(collection: function[]): function;

17

```

18

19

**Usage Examples:**

20

21

```javascript

22

import { compile, serialize, middleware, prefixer, stringify } from 'stylis';

23

24

// Single middleware

25

const withPrefixing = serialize(

26

compile('div { display: flex; }'),

27

middleware([prefixer, stringify])

28

);

29

30

// Multiple middleware in sequence

31

const processed = serialize(

32

compile('.component { user-select: none; }'),

33

middleware([

34

customMiddleware,

35

prefixer,

36

namespace,

37

stringify

38

])

39

);

40

41

// Custom middleware example

42

const loggerMiddleware = (element, index, children, callback) => {

43

console.log(`Processing ${element.type}: ${element.value}`);

44

return stringify(element, index, children, callback);

45

};

46

```

47

48

### Prefixer Middleware

49

50

Built-in middleware that adds vendor prefixes to CSS properties and values for cross-browser compatibility.

51

52

```javascript { .api }

53

/**

54

* Vendor prefixing middleware for cross-browser compatibility

55

* @param element - AST node to process

56

* @param index - Index in children array

57

* @param children - Sibling nodes

58

* @param callback - Recursive callback function

59

* @returns void (modifies element.return property)

60

*/

61

function prefixer(element: object, index: number, children: object[], callback: function): void;

62

```

63

64

**Supported Properties:**

65

- **Flexbox**: `display: flex`, `flex-direction`, `justify-content`, `align-items`, etc.

66

- **Grid**: `display: grid`, `grid-template-columns`, `grid-gap`, etc.

67

- **Transforms**: `transform`, `transform-origin`

68

- **Animations**: `animation`, `transition`, `@keyframes`

69

- **User Interface**: `user-select`, `appearance`, `tab-size`

70

- **Layout**: `position: sticky`, `writing-mode`

71

- **Visual**: `mask`, `clip-path`, `filter`, `backdrop-filter`

72

73

**Usage Examples:**

74

75

```javascript

76

// Flexbox prefixing

77

serialize(compile('div { display: flex; }'), middleware([prefixer, stringify]));

78

// Output includes: -webkit-box, -ms-flexbox, flex

79

80

// Animation prefixing

81

serialize(compile('@keyframes slide { from { opacity: 0; } }'), middleware([prefixer, stringify]));

82

// Output includes: @-webkit-keyframes

83

84

// Pseudo-selector prefixing

85

serialize(compile('input::placeholder { color: gray; }'), middleware([prefixer, stringify]));

86

// Output includes: ::-webkit-input-placeholder, ::-moz-placeholder, etc.

87

```

88

89

### Namespace Middleware

90

91

Scopes CSS selectors to prevent style conflicts by adding namespace prefixes or transforming selectors.

92

93

```javascript { .api }

94

/**

95

* CSS selector namespacing middleware for style isolation

96

* @param element - AST node to process (modifies element.props for rulesets)

97

* @returns void (modifies element in place)

98

*/

99

function namespace(element: object): void;

100

```

101

102

**Features:**

103

- **Selector Scoping**: Adds parent selector to scope styles

104

- **Global Escaping**: Handles `:global()` selectors to bypass scoping

105

- **Combinator Handling**: Properly processes child, adjacent, and general sibling combinators

106

107

**Usage Examples:**

108

109

```javascript

110

// Basic namespacing (requires setup with namespace identifier)

111

serialize(compile('.button { color: blue; }'), middleware([namespace, stringify]));

112

113

// Global selector escaping

114

serialize(compile(':global(.external) { margin: 0; }'), middleware([namespace, stringify]));

115

```

116

117

### Rulesheet Helper

118

119

Creates middleware for processing complete CSS rules after they are fully processed.

120

121

```javascript { .api }

122

/**

123

* Create middleware for processing complete CSS rules

124

* @param callback - Function to call with complete rule strings

125

* @returns Middleware function

126

*/

127

function rulesheet(callback: function): function;

128

```

129

130

**Usage Examples:**

131

132

```javascript

133

// Collect all generated CSS rules

134

const rules = [];

135

const collector = rulesheet((rule) => {

136

rules.push(rule);

137

});

138

139

serialize(compile('h1 { color: red; } p { color: blue; }'), middleware([collector, stringify]));

140

console.log(rules); // ['h1{color:red;}', 'p{color:blue;}']

141

142

// Rule validation middleware

143

const validator = rulesheet((rule) => {

144

if (rule.includes('!important')) {

145

console.warn('Important declaration found:', rule);

146

}

147

});

148

```

149

150

## Custom Middleware Development

151

152

### Middleware Interface

153

154

All middleware functions follow this interface:

155

156

```javascript { .api }

157

/**

158

* Middleware function interface

159

* @param element - Current AST node being processed

160

* @param index - Index of element in parent's children array

161

* @param children - Array of sibling elements

162

* @param callback - Recursive callback to process child elements

163

* @returns CSS string output or void (for element modification)

164

*/

165

interface MiddlewareFunction {

166

(element: object, index: number, children: object[], callback: function): string | void;

167

}

168

```

169

170

### Element Modification Patterns

171

172

Middleware can modify elements in several ways:

173

174

```javascript

175

// Modify element properties

176

const propertyMiddleware = (element) => {

177

if (element.type === 'decl' && element.props === 'color') {

178

element.children = 'blue'; // Change all colors to blue

179

}

180

};

181

182

// Add to element.return for output

183

const outputMiddleware = (element, index, children, callback) => {

184

if (element.type === 'rule') {

185

element.return = `/* Generated rule */\n${stringify(element, index, children, callback)}`;

186

}

187

};

188

189

// Generate additional rules

190

const duplicatorMiddleware = (element, index, children, callback) => {

191

if (element.type === 'rule') {

192

const original = stringify(element, index, children, callback);

193

const duplicate = original.replace(element.props[0], element.props[0] + '-copy');

194

return original + duplicate;

195

}

196

return stringify(element, index, children, callback);

197

};

198

```

199

200

### Common Middleware Patterns

201

202

#### Property Transformation

203

```javascript

204

const unitConverter = (element) => {

205

if (element.type === 'decl') {

206

// Convert px to rem

207

element.children = element.children.replace(/(\d+)px/g, (match, num) => {

208

return (parseInt(num) / 16) + 'rem';

209

});

210

}

211

};

212

```

213

214

#### Selector Modification

215

```javascript

216

const selectorPrefix = (element) => {

217

if (element.type === 'rule') {

218

element.props = element.props.map(selector => `.namespace ${selector}`);

219

}

220

};

221

```

222

223

#### Conditional Processing

224

```javascript

225

const responsiveMiddleware = (element, index, children, callback) => {

226

if (element.type === 'rule' && element.props.some(prop => prop.includes('.mobile'))) {

227

// Wrap mobile styles in media query

228

return `@media (max-width: 768px) { ${stringify(element, index, children, callback)} }`;

229

}

230

return stringify(element, index, children, callback);

231

};

232

```

233

234

## Middleware Execution Order

235

236

Middleware functions execute in the order they appear in the collection array:

237

238

1. **Pre-processing**: Transform AST nodes before output generation

239

2. **Output Generation**: Generate CSS strings (typically `stringify`)

240

3. **Post-processing**: Transform generated CSS strings

241

242

**Best Practices:**

243

- Place element modification middleware before `stringify`

244

- Place output transformation middleware after `stringify`

245

- Use `rulesheet` for final rule collection and validation

246

- Keep middleware functions focused on single responsibilities

247

248

## Error Handling in Middleware

249

250

Middleware should handle errors gracefully to avoid breaking the entire processing pipeline:

251

252

```javascript

253

const safeMiddleware = (element, index, children, callback) => {

254

try {

255

// Middleware logic here

256

return customProcessing(element, index, children, callback);

257

} catch (error) {

258

console.warn('Middleware error:', error);

259

// Fallback to default processing

260

return stringify(element, index, children, callback);

261

}

262

};

263

```