or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mdcustom-injection.mddevelopment-mode.mdindex.md

custom-injection.mddocs/

0

# Custom Injection

1

2

Advanced customization of CSS injection behavior with custom JavaScript code and runtime functions.

3

4

## Capabilities

5

6

### Custom Injection Types

7

8

Type definitions for custom CSS injection functions.

9

10

```typescript { .api }

11

/**

12

* Function that returns JavaScript code string for CSS injection

13

* @param cssCode - CSS code to inject (as string literal)

14

* @param options - Injection configuration options

15

* @returns JavaScript code string that will inject the CSS

16

*/

17

type InjectCode = (cssCode: string, options: InjectCodeOptions) => string;

18

19

/**

20

* Runtime function that directly injects CSS into the DOM

21

* @param cssCode - CSS code to inject

22

* @param options - Injection configuration options

23

*/

24

type InjectCodeFunction = (cssCode: string, options: InjectCodeOptions) => void;

25

26

/**

27

* Options passed to injection functions

28

*/

29

interface InjectCodeOptions {

30

/** Style element ID or generator function */

31

styleId?: string | (() => string);

32

/** Enable strict CSP support with nonce */

33

useStrictCSP?: boolean;

34

/** Additional attributes for the style element */

35

attributes?: { [key: string]: string } | undefined;

36

}

37

```

38

39

### Custom Injection Code

40

41

Provide a function that returns JavaScript code for CSS injection.

42

43

```typescript { .api }

44

/**

45

* Custom CSS injection code function

46

* Returns JavaScript code that will be executed to inject CSS

47

*/

48

injectCode?: InjectCode;

49

```

50

51

**Usage Examples:**

52

53

```typescript

54

import { defineConfig } from "vite";

55

import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js";

56

57

// Basic custom injection code

58

export default defineConfig({

59

plugins: [

60

cssInjectedByJsPlugin({

61

injectCode: (cssCode, options) => {

62

return `

63

try {

64

if (typeof document !== 'undefined') {

65

var style = document.createElement('style');

66

${options.styleId ? `style.id = '${options.styleId}';` : ''}

67

style.appendChild(document.createTextNode(${cssCode}));

68

document.head.appendChild(style);

69

}

70

} catch (e) {

71

console.error('CSS injection failed:', e);

72

}

73

`;

74

},

75

}),

76

],

77

});

78

79

// Advanced injection with error handling and CSP

80

export default defineConfig({

81

plugins: [

82

cssInjectedByJsPlugin({

83

injectCode: (cssCode, options) => {

84

let attributeCode = '';

85

86

// Add style ID if provided

87

if (options.styleId) {

88

attributeCode += `style.id = '${options.styleId}';`;

89

}

90

91

// Add CSP nonce if enabled

92

if (options.useStrictCSP) {

93

attributeCode += `

94

var nonceMeta = document.head.querySelector('meta[property="csp-nonce"]');

95

if (nonceMeta) style.nonce = nonceMeta.content;

96

`;

97

}

98

99

// Add custom attributes

100

if (options.attributes) {

101

for (const [key, value] of Object.entries(options.attributes)) {

102

attributeCode += `style.setAttribute('${key}', '${value}');`;

103

}

104

}

105

106

return `

107

try {

108

if (typeof document !== 'undefined') {

109

var style = document.createElement('style');

110

${attributeCode}

111

style.appendChild(document.createTextNode(${cssCode}));

112

document.head.appendChild(style);

113

}

114

} catch (e) {

115

console.error('vite-plugin-css-injected-by-js injection failed:', e);

116

}

117

`;

118

},

119

}),

120

],

121

});

122

```

123

124

### Custom Injection Runtime Function

125

126

Provide a runtime function that directly injects CSS into the DOM.

127

128

```typescript { .api }

129

/**

130

* Custom CSS injection runtime function

131

* Executed directly at runtime to inject CSS

132

*/

133

injectCodeFunction?: InjectCodeFunction;

134

```

135

136

**Usage Examples:**

137

138

```typescript

139

// Basic runtime injection function

140

export default defineConfig({

141

plugins: [

142

cssInjectedByJsPlugin({

143

injectCodeFunction: (cssCode, options) => {

144

try {

145

if (typeof document !== 'undefined') {

146

const style = document.createElement('style');

147

148

// Set style ID if provided

149

if (options.styleId) {

150

style.id = options.styleId;

151

}

152

153

// Set CSP nonce if enabled

154

if (options.useStrictCSP) {

155

const nonceMeta = document.head.querySelector('meta[property="csp-nonce"]');

156

if (nonceMeta) {

157

style.nonce = nonceMeta.content;

158

}

159

}

160

161

// Set custom attributes

162

if (options.attributes) {

163

for (const [key, value] of Object.entries(options.attributes)) {

164

style.setAttribute(key, value);

165

}

166

}

167

168

style.appendChild(document.createTextNode(cssCode));

169

document.head.appendChild(style);

170

}

171

} catch (error) {

172

console.error('CSS injection failed:', error);

173

}

174

},

175

}),

176

],

177

});

178

179

// Advanced runtime function with insertion control

180

export default defineConfig({

181

plugins: [

182

cssInjectedByJsPlugin({

183

injectCodeFunction: (cssCode, options) => {

184

try {

185

if (typeof document !== 'undefined') {

186

const style = document.createElement('style');

187

188

// Configure style element

189

if (options.styleId) {

190

style.id = options.styleId;

191

}

192

193

// Add all custom attributes

194

if (options.attributes) {

195

Object.entries(options.attributes).forEach(([key, value]) => {

196

style.setAttribute(key, value);

197

});

198

}

199

200

// Handle CSP nonce

201

if (options.useStrictCSP) {

202

const nonceMeta = document.head.querySelector('meta[property="csp-nonce"]');

203

if (nonceMeta?.content) {

204

style.nonce = nonceMeta.content;

205

}

206

}

207

208

// Insert CSS content

209

style.appendChild(document.createTextNode(cssCode));

210

211

// Insert at specific position (after title if exists)

212

const title = document.head.querySelector('title');

213

if (title) {

214

document.head.insertBefore(style, title.nextSibling);

215

} else {

216

document.head.appendChild(style);

217

}

218

}

219

} catch (error) {

220

console.error('vite-plugin-css-injected-by-js:', error);

221

}

222

},

223

}),

224

],

225

});

226

```

227

228

### Injection Options

229

230

Configuration options passed to custom injection functions.

231

232

```typescript { .api }

233

/**

234

* Options passed to injection functions

235

*/

236

interface InjectCodeOptions {

237

/** Style element ID or generator function */

238

styleId?: string | (() => string);

239

/** Enable strict CSP support with nonce */

240

useStrictCSP?: boolean;

241

/** Additional attributes for the style element */

242

attributes?: { [key: string]: string } | undefined;

243

}

244

```

245

246

**Option Details:**

247

248

- **`styleId`**: Sets the `id` attribute on the created style element

249

- **`useStrictCSP`**: Enables Content Security Policy support by reading nonce from meta tag

250

- **`attributes`**: Additional attributes to set on the style element (includes development mode attributes)

251

252

### Development Mode Integration

253

254

Custom injection functions work with development mode and receive additional attributes:

255

256

```typescript

257

// Development mode aware injection

258

injectCodeFunction: (cssCode, options) => {

259

try {

260

if (typeof document !== 'undefined') {

261

const style = document.createElement('style');

262

263

// Handle all attributes (including dev mode attributes)

264

if (options.attributes) {

265

Object.entries(options.attributes).forEach(([key, value]) => {

266

style.setAttribute(key, value);

267

});

268

}

269

270

// In development mode, attributes will include:

271

// { "data-vite-dev-id": "path/to/file.css" }

272

273

style.appendChild(document.createTextNode(cssCode));

274

document.head.appendChild(style);

275

}

276

} catch (error) {

277

console.error('CSS injection failed:', error);

278

}

279

}

280

```

281

282

### Content Security Policy Support

283

284

When `useStrictCSP` is enabled, injection functions should read the nonce from a meta tag:

285

286

```typescript

287

// CSP-compliant injection

288

injectCodeFunction: (cssCode, options) => {

289

try {

290

if (typeof document !== 'undefined') {

291

const style = document.createElement('style');

292

293

if (options.useStrictCSP) {

294

// Read nonce from meta tag

295

const nonceMeta = document.head.querySelector('meta[property="csp-nonce"]');

296

if (nonceMeta?.content) {

297

style.nonce = nonceMeta.content;

298

}

299

}

300

301

style.appendChild(document.createTextNode(cssCode));

302

document.head.appendChild(style);

303

}

304

} catch (error) {

305

console.error('CSS injection failed:', error);

306

}

307

}

308

```

309

310

**Required HTML meta tag:**

311

```html

312

<meta property="csp-nonce" content="your-nonce-value" />

313

```

314

315

### Error Handling Best Practices

316

317

Custom injection functions should include proper error handling:

318

319

```typescript

320

injectCodeFunction: (cssCode, options) => {

321

try {

322

// Check for document availability (SSR compatibility)

323

if (typeof document === 'undefined') {

324

return;

325

}

326

327

// Create and configure style element

328

const style = document.createElement('style');

329

330

// Apply all options safely

331

if (options.styleId) {

332

style.id = options.styleId;

333

}

334

335

if (options.attributes) {

336

Object.entries(options.attributes).forEach(([key, value]) => {

337

try {

338

style.setAttribute(key, value);

339

} catch (attrError) {

340

console.warn(`Failed to set attribute ${key}:`, attrError);

341

}

342

});

343

}

344

345

if (options.useStrictCSP) {

346

const nonceMeta = document.head.querySelector('meta[property="csp-nonce"]');

347

if (nonceMeta?.content) {

348

style.nonce = nonceMeta.content;

349

}

350

}

351

352

// Insert CSS content

353

style.appendChild(document.createTextNode(cssCode));

354

document.head.appendChild(style);

355

356

} catch (error) {

357

// Use the plugin's error prefix for consistency

358

console.error('vite-plugin-css-injected-by-js:', error);

359

}

360

}

361

```