@scania/tegel-lite 0.0.1 → 0.0.2-rules-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2045 @@
1
+ # @scania/tegel-lite — AI Coding Rules
2
+
3
+ > Auto-generated by `npm run generate:ai-rules`. Do not edit manually.
4
+
5
+ > Generated: 2026-02-13
6
+
7
+ ## 01-design-tokens
8
+
9
+ # Design Tokens & Variables
10
+
11
+ ## RULE: ALWAYS use design tokens instead of hardcoded values.
12
+
13
+ ## Primitive Color Tokens (from scania-variables.css)
14
+
15
+ ### Blue (Primary Brand)
16
+
17
+ | Token | Value |
18
+ | ------------------------- | --------- |
19
+ | `--scania-color-blue-50` | `#d8dde5` |
20
+ | `--scania-color-blue-100` | `#bacde8` |
21
+ | `--scania-color-blue-200` | `#87afe8` |
22
+ | `--scania-color-blue-300` | `#4a89f3` |
23
+ | `--scania-color-blue-400` | `#2a6ecf` |
24
+ | `--scania-color-blue-500` | `#2058a8` |
25
+ | `--scania-color-blue-600` | `#16417f` |
26
+ | `--scania-color-blue-700` | `#0f3263` |
27
+ | `--scania-color-blue-800` | `#041e42` |
28
+ | `--scania-color-blue-900` | `#001533` |
29
+
30
+ ### Red (Secondary Brand / Danger)
31
+
32
+ | Token | Value |
33
+ | ------------------------ | --------- |
34
+ | `--scania-color-red-50` | `#ffdfe3` |
35
+ | `--scania-color-red-100` | `#ffb9b8` |
36
+ | `--scania-color-red-200` | `#fc8a88` |
37
+ | `--scania-color-red-300` | `#ea4851` |
38
+ | `--scania-color-red-400` | `#d1001b` |
39
+ | `--scania-color-red-500` | `#b20018` |
40
+ | `--scania-color-red-600` | `#8a0012` |
41
+ | `--scania-color-red-700` | `#6c0011` |
42
+ | `--scania-color-red-800` | `#480008` |
43
+ | `--scania-color-red-900` | `#350004` |
44
+
45
+ ### Green (Success)
46
+
47
+ | Token | Value |
48
+ | -------------------------- | --------- |
49
+ | `--scania-color-green-50` | `#d3eee7` |
50
+ | `--scania-color-green-100` | `#9bd9c2` |
51
+ | `--scania-color-green-200` | `#65bda5` |
52
+ | `--scania-color-green-300` | `#009e7e` |
53
+ | `--scania-color-green-400` | `#008064` |
54
+ | `--scania-color-green-500` | `#00664f` |
55
+ | `--scania-color-green-600` | `#004c3b` |
56
+ | `--scania-color-green-700` | `#0a382e` |
57
+ | `--scania-color-green-800` | `#00251b` |
58
+ | `--scania-color-green-900` | `#001a12` |
59
+
60
+ ### Yellow (Warning)
61
+
62
+ | Token | Value |
63
+ | --------------------------- | --------- |
64
+ | `--scania-color-yellow-50` | `#f9eec3` |
65
+ | `--scania-color-yellow-100` | `#f8e596` |
66
+ | `--scania-color-yellow-200` | `#f4d65d` |
67
+ | `--scania-color-yellow-300` | `#f1c21b` |
68
+ | `--scania-color-yellow-400` | `#eaad06` |
69
+ | `--scania-color-yellow-500` | `#b87c14` |
70
+ | `--scania-color-yellow-600` | `#975717` |
71
+ | `--scania-color-yellow-700` | `#6b3b10` |
72
+ | `--scania-color-yellow-800` | `#4d260a` |
73
+ | `--scania-color-yellow-900` | `#361b07` |
74
+
75
+ ### Grey (Neutral)
76
+
77
+ | Token | Value |
78
+ | -------------------------- | --------- |
79
+ | `--scania-color-grey-00` | `#fff` |
80
+ | `--scania-color-grey-50` | `#f6f7f9` |
81
+ | `--scania-color-grey-100` | `#edeff3` |
82
+ | `--scania-color-grey-150` | `#e0e5eb` |
83
+ | `--scania-color-grey-200` | `#d1d7e0` |
84
+ | `--scania-color-grey-250` | `#b4becd` |
85
+ | `--scania-color-grey-300` | `#93a2b7` |
86
+ | `--scania-color-grey-350` | `#7e90a9` |
87
+ | `--scania-color-grey-400` | `#6e829f` |
88
+ | `--scania-color-grey-500` | `#5f728c` |
89
+ | `--scania-color-grey-600` | `#4e5e75` |
90
+ | `--scania-color-grey-700` | `#3a4554` |
91
+ | `--scania-color-grey-800` | `#242c37` |
92
+ | `--scania-color-grey-850` | `#1c222b` |
93
+ | `--scania-color-grey-900` | `#15181d` |
94
+ | `--scania-color-grey-950` | `#0e1013` |
95
+ | `--scania-color-grey-1000` | `#000` |
96
+
97
+ ## Semantic Color Tokens (Light Mode)
98
+
99
+ Use these instead of primitive colors in layouts:
100
+
101
+ ### Backgrounds
102
+
103
+ | Token | Usage |
104
+ | -------------------------------------------------------------------------- | ------------------------------------- |
105
+ | `--color-background-base` | Page/app base background |
106
+ | `--color-background-layer-01` | Primary card/section background |
107
+ | `--color-background-layer-02` | Secondary/nested background |
108
+ | `--color-background-layer-03` | Third-level nested background |
109
+ | `--color-background-overlay` | Modal/drawer backdrop |
110
+ | `--color-background-top` | Top-level surface |
111
+ | `--color-background-accent-primary-default` | Primary button background |
112
+ | `--color-background-accent-primary-hover` | Primary button hover |
113
+ | `--color-background-accent-primary-pressed` | Primary button pressed |
114
+ | `--color-background-accent-primary-disabled` | Disabled button background |
115
+ | `--color-background-accent-danger-default` | Danger action background |
116
+ | `--color-background-surface-100` through `--color-background-surface-1000` | Surface levels for subtle backgrounds |
117
+
118
+ ### Text (Foreground)
119
+
120
+ | Token | Usage |
121
+ | ---------------------------------------- | ---------------------- |
122
+ | `--color-foreground-text-strong` | Primary text, headings |
123
+ | `--color-foreground-text-defined` | Secondary text, labels |
124
+ | `--color-foreground-text-soft` | Tertiary text |
125
+ | `--color-foreground-text-subtle` | Placeholder, hint text |
126
+ | `--color-foreground-text-disabled` | Disabled text |
127
+ | `--color-foreground-text-accent-default` | Link text |
128
+ | `--color-foreground-text-accent-visited` | Visited link |
129
+ | `--color-foreground-text-brand` | Brand-colored text |
130
+
131
+ ### Borders
132
+
133
+ | Token | Usage |
134
+ | ---------------------------------------- | ---------------------- |
135
+ | `--color-foreground-border-strong` | Strong visible borders |
136
+ | `--color-foreground-border-soft` | Default input borders |
137
+ | `--color-foreground-border-subtle` | Hover borders |
138
+ | `--color-foreground-border-discrete` | Divider lines |
139
+ | `--color-foreground-border-accent-focus` | Focus ring color |
140
+ | `--color-foreground-border-disabled` | Disabled borders |
141
+
142
+ ### Icons
143
+
144
+ | Token | Usage |
145
+ | ---------------------------------- | --------------- |
146
+ | `--color-foreground-icon-strong` | Primary icons |
147
+ | `--color-foreground-icon-defined` | Secondary icons |
148
+ | `--color-foreground-icon-soft` | Tertiary icons |
149
+ | `--color-foreground-icon-subtle` | Faint icons |
150
+ | `--color-foreground-icon-disabled` | Disabled icons |
151
+
152
+ ### System / Status Colors
153
+
154
+ | Token | Usage |
155
+ | --------------------------------- | ------------------------- |
156
+ | `--color-system-success-default` | Success states (green) |
157
+ | `--color-system-success-discrete` | Success background tint |
158
+ | `--color-system-warning-default` | Warning states (yellow) |
159
+ | `--color-system-warning-discrete` | Warning background tint |
160
+ | `--color-system-danger-default` | Error/danger states (red) |
161
+ | `--color-system-danger-discrete` | Error background tint |
162
+ | `--color-system-info-default` | Info states (blue) |
163
+ | `--color-system-info-discrete` | Info background tint |
164
+
165
+ ## Spacing Units
166
+
167
+ | Token | Value |
168
+ | ------------------- | ------- |
169
+ | `--scania-unit-0` | `0` |
170
+ | `--scania-unit-1` | `1px` |
171
+ | `--scania-unit-2` | `2px` |
172
+ | `--scania-unit-4` | `4px` |
173
+ | `--scania-unit-8` | `8px` |
174
+ | `--scania-unit-12` | `12px` |
175
+ | `--scania-unit-16` | `16px` |
176
+ | `--scania-unit-20` | `20px` |
177
+ | `--scania-unit-24` | `24px` |
178
+ | `--scania-unit-32` | `32px` |
179
+ | `--scania-unit-40` | `40px` |
180
+ | `--scania-unit-48` | `48px` |
181
+ | `--scania-unit-56` | `56px` |
182
+ | `--scania-unit-64` | `64px` |
183
+ | `--scania-unit-80` | `80px` |
184
+ | `--scania-unit-96` | `96px` |
185
+ | `--scania-unit-120` | `120px` |
186
+
187
+ ## Dimension/Spacing Semantic Tokens
188
+
189
+ | Token | Value | Usage |
190
+ | -------------------------- | ------ | ----------------------- |
191
+ | `--dimension-space-tiny` | `4px` | Micro spacing |
192
+ | `--dimension-space-small` | `16px` | Default element spacing |
193
+ | `--dimension-space-medium` | `24px` | Section padding |
194
+ | `--dimension-space-large` | `48px` | Large gaps |
195
+ | `--dimension-space-huge` | `80px` | Hero sections |
196
+
197
+ ## Border Radius Tokens
198
+
199
+ | Token | Value | Usage |
200
+ | ------------------------------------------ | -------- | -------------------------- |
201
+ | `--dimension-radius-narrow` | `4px` | Buttons, inputs, cards |
202
+ | `--dimension-radius-soft` | `8px` | Larger containers |
203
+ | `--dimension-radius-rounded` | `32px` | Chips, pills |
204
+ | `--dimension-radius-circle` | `9999px` | Avatars, circular elements |
205
+ | `--dimension-radius-for-surface-clickable` | `4px` | Clickable elements |
206
+ | `--dimension-radius-for-surface-field` | `4px` | Form fields |
207
+
208
+ ## Font Families
209
+
210
+ | Token | Value | Usage |
211
+ | ------------------------------------- | ---------------------------- | ----------------------- |
212
+ | `--scania-font-family-display` | `scania sans headline` | Display/headline text |
213
+ | `--scania-font-family-default` | `scania sans` | Body/default text |
214
+ | `--scania-font-family-semi-condensed` | `scania sans semi condensed` | Side menu, compact text |
215
+
216
+ ## Font Weights
217
+
218
+ | Token | Value |
219
+ | ------------------------------ | ----- |
220
+ | `--scania-font-weight-regular` | `400` |
221
+ | `--scania-font-weight-bold` | `700` |
222
+
223
+ ## Motion/Animation Tokens
224
+
225
+ | Token | Value | Usage |
226
+ | ----------------------------------- | -------------------------------- | -------------------- |
227
+ | `--tds-motion-duration-fast-01` | `100ms` | Micro-interactions |
228
+ | `--tds-motion-duration-fast-02` | `150ms` | Quick transitions |
229
+ | `--tds-motion-duration-moderate-01` | `200ms` | Standard transitions |
230
+ | `--tds-motion-duration-moderate-02` | `300ms` | Moderate animations |
231
+ | `--tds-motion-duration-slow-01` | `400ms` | Complex animations |
232
+ | `--tds-motion-duration-slow-02` | `500ms` | Full animations |
233
+ | `--tds-motion-easing-scania` | `cubic-bezier(0.4, 0, 0, 1)` | Default easing |
234
+ | `--tds-motion-easing-enter` | `cubic-bezier(0.1, 0.9, 0.2, 1)` | Enter transitions |
235
+ | `--tds-motion-easing-exit` | `cubic-bezier(0.7, 0, 1, 0.5)` | Exit transitions |
236
+
237
+ ---
238
+
239
+ # Typography
240
+
241
+ ## Typography Scale (defined in scania-variables.css under `.scania`)
242
+
243
+ ### Display (Hero/Marketing)
244
+
245
+ - **Display 01**: 216px / 183px LH / uppercase / Scania Sans Headline Bold
246
+ - **Display 02**: 156px / 148px LH / uppercase / Scania Sans Headline Bold
247
+ - **Display 03**: 112px / 106px LH / uppercase / Scania Sans Headline Bold
248
+ - **Display 04**: 80px / 80px LH / uppercase / Scania Sans Headline Bold
249
+ - **Display 05**: 56px / 56px LH / uppercase / Scania Sans Headline Bold
250
+
251
+ ### Headlines
252
+
253
+ - **Headline 01**: 40px / 40px LH / uppercase / Scania Sans Headline Bold
254
+ - **Headline 02**: 32px / 32px LH / Scania Sans Bold / ls: -1.28px
255
+ - **Headline 03**: 24px / 24px LH / Scania Sans Bold / ls: -0.72px
256
+ - **Headline 04**: 20px / 24px LH / Scania Sans Bold / ls: -0.4px
257
+ - **Headline 05**: 16px / 24px LH / Scania Sans Bold / ls: -0.32px
258
+ - **Headline 06**: 14px / 16px LH / uppercase / Scania Sans Bold / ls: -0.28px
259
+
260
+ ### Paragraphs / Body
261
+
262
+ - **Paragraph 01**: 24px / 32px LH / Scania Sans Regular / ls: -0.72px
263
+ - **Paragraph 02**: 20px / 24px LH / Scania Sans Regular / ls: -0.2px
264
+ - **Body 01**: 16px / 24px LH / Scania Sans Regular / ls: -0.32px
265
+ - **Body 02**: 14px / 20px LH / Scania Sans Regular / ls: -0.14px
266
+
267
+ ### Component Text
268
+
269
+ - **Component Text 01**: 14px / 16px LH / ls: -0.14px
270
+ - **Component Text 02**: 14px / 20px LH / ls: -0.14px
271
+ - **Component Label 01**: 12px / 16px LH / ls: 0
272
+ - **Component Label 02**: 10px / 8px LH / ls: 0
273
+ - **Component Title 01**: 14px / 16px LH / ls: -0.14px
274
+ - **Component Title 02**: 10px / 16px LH / uppercase / ls: 0.8px
275
+
276
+ ### Links
277
+
278
+ - **Link 01**: 16px / 24px LH / ls: -0.32px
279
+ - **Link 02**: 14px / 20px LH / ls: -0.14px
280
+
281
+ ## Typography Usage in Components
282
+
283
+ Components use internal typography tokens like `--detail-02-*`, `--headline-07-*`, `--body-01-*` etc. These are automatically applied. Do NOT manually set font properties on Tegel components.
284
+
285
+ ---
286
+
287
+ # Layout & Grid
288
+
289
+ Tegel Lite does not include a CSS grid utility system. Use standard CSS Grid/Flexbox with Tegel tokens:
290
+
291
+ ```html
292
+ <div
293
+ class="scania tds-mode-light"
294
+ style="
295
+ display: grid;
296
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
297
+ gap: var(--dimension-space-small);
298
+ padding: var(--dimension-space-medium);
299
+ "
300
+ >
301
+ <!-- Grid items -->
302
+ </div>
303
+ ```
304
+
305
+ ## Layout Components
306
+
307
+ - **Header** (`.tl-header`) – Full-width app bar, fixed at top
308
+ - **Side Menu** (`.tl-side-menu`) – Navigation sidebar, collapsible
309
+ - **Footer** (`.tl-footer`) – Multi-section page footer
310
+
311
+ ## Breakpoints (used in components)
312
+
313
+ | Breakpoint | Value | Usage |
314
+ | ---------- | -------- | ------------- |
315
+ | xs | `320px` | Mobile |
316
+ | sm | `384px` | Small mobile |
317
+ | md | `672px` | Tablet |
318
+ | lg | `992px` | Desktop |
319
+ | xl | `1056px` | Large desktop |
320
+ | xxl | `1312px` | Wide desktop |
321
+ | xxxl | `1584px` | Ultra-wide |
322
+
323
+ ---
324
+
325
+ ## 02-components-forms
326
+
327
+ # Form Components
328
+
329
+ ## Button
330
+
331
+ **Classes**: `tl-button`, `tl-button--primary`, `tl-button--secondary`, `tl-button--ghost`, `tl-button--danger`
332
+ **Sizes**: `tl-button--xs` (24px), `tl-button--sm` (40px), `tl-button--md` (48px), `tl-button--lg` (56px)
333
+ **Modifiers**: `tl-button--full-width`, `tl-button--icon`, `tl-button--only-icon`
334
+
335
+ ```html
336
+ <!-- Primary button (default) -->
337
+ <button class="tl-button tl-button--primary tl-button--md">
338
+ Primary Action
339
+ </button>
340
+
341
+ <!-- Secondary button -->
342
+ <button class="tl-button tl-button--secondary tl-button--md">Secondary</button>
343
+
344
+ <!-- Ghost button -->
345
+ <button class="tl-button tl-button--ghost tl-button--sm">Ghost</button>
346
+
347
+ <!-- Danger button -->
348
+ <button class="tl-button tl-button--danger tl-button--md">Delete</button>
349
+
350
+ <!-- Button with icon (text BEFORE icon) -->
351
+ <button class="tl-button tl-button--primary tl-button--icon tl-button--md">
352
+ Save
353
+ <span class="tl-icon tl-icon--save" aria-hidden="true"></span>
354
+ </button>
355
+
356
+ <!-- Icon-only button -->
357
+ <button
358
+ class="tl-button tl-button--only-icon tl-button--md"
359
+ aria-label="Settings"
360
+ >
361
+ <span class="tl-icon tl-icon--settings"></span>
362
+ </button>
363
+
364
+ <!-- Full width -->
365
+ <button
366
+ class="tl-button tl-button--primary tl-button--md tl-button--full-width"
367
+ >
368
+ Full Width
369
+ </button>
370
+
371
+ <!-- Disabled -->
372
+ <button class="tl-button tl-button--primary tl-button--md" disabled>
373
+ Disabled
374
+ </button>
375
+ ```
376
+
377
+ **Structure Rules**:
378
+
379
+ - **Button with icon**: Text comes FIRST, then icon. Add `tl-button--icon` modifier. Icon should have `aria-hidden="true"`.
380
+ - **Icon-only button**: Add `tl-button--only-icon` modifier and `aria-label` for accessibility.
381
+ - **Size**: Always include a size modifier (--xs, --sm, --md, --lg).
382
+ - **Variant**: Primary, secondary, ghost, or danger (optional, defaults to primary).
383
+
384
+ **Do's**:
385
+
386
+ - Always put text before icon in buttons with icons
387
+ - Always add `aria-hidden="true"` to decorative icons in buttons with text
388
+ - Always use `aria-label` for icon-only buttons
389
+
390
+ **Don'ts**:
391
+
392
+ - Never use `<div>` as a button
393
+ - Never style buttons with custom colors
394
+ - Never put icon before text in buttons with text
395
+
396
+ ---
397
+
398
+ ## Text Field (Input)
399
+
400
+ **Classes**: `tl-text-field`, `tl-text-field--lg` (56px, default), `tl-text-field--md` (48px), `tl-text-field--sm` (40px)
401
+ **Variants**: `tl-text-field--primary`, `tl-text-field--secondary`, `tl-text-field--label-inside`
402
+ **States**: `tl-text-field--error`, `tl-text-field--success`
403
+
404
+ ```html
405
+ <!-- Basic text field with external label -->
406
+ <div class="tl-text-field tl-text-field--lg">
407
+ <label class="tl-text-field__label" for="name">Name</label>
408
+ <input
409
+ class="tl-text-field__input"
410
+ id="name"
411
+ type="text"
412
+ placeholder="Enter your name"
413
+ />
414
+ </div>
415
+
416
+ <!-- Label inside -->
417
+ <div class="tl-text-field tl-text-field--lg tl-text-field--label-inside">
418
+ <label class="tl-text-field__label" for="email">Email</label>
419
+ <input class="tl-text-field__input" id="email" type="email" placeholder=" " />
420
+ </div>
421
+
422
+ <!-- With helper text -->
423
+ <div class="tl-text-field tl-text-field--lg">
424
+ <label class="tl-text-field__label" for="phone">Phone</label>
425
+ <input
426
+ class="tl-text-field__input"
427
+ id="phone"
428
+ type="tel"
429
+ placeholder="+46..."
430
+ />
431
+ <div class="tl-text-field__bottom">
432
+ <span class="tl-text-field__helper">Include country code</span>
433
+ </div>
434
+ </div>
435
+
436
+ <!-- Error state -->
437
+ <div class="tl-text-field tl-text-field--lg tl-text-field--error">
438
+ <label class="tl-text-field__label" for="req">Required field</label>
439
+ <input class="tl-text-field__input" id="req" type="text" placeholder=" " />
440
+ <div class="tl-text-field__bottom">
441
+ <span class="tl-text-field__helper">This field is required</span>
442
+ </div>
443
+ </div>
444
+
445
+ <!-- With prefix/suffix icons -->
446
+ <div class="tl-text-field tl-text-field--lg">
447
+ <label class="tl-text-field__label" for="search">Search</label>
448
+ <span class="tl-text-field__prefix--icon tl-icon tl-icon--search"></span>
449
+ <input
450
+ class="tl-text-field__input"
451
+ id="search"
452
+ type="text"
453
+ placeholder="Search..."
454
+ />
455
+ </div>
456
+
457
+ <!-- With character counter -->
458
+ <div class="tl-text-field tl-text-field--lg">
459
+ <label class="tl-text-field__label" for="bio">Bio</label>
460
+ <input
461
+ class="tl-text-field__input"
462
+ id="bio"
463
+ type="text"
464
+ placeholder=" "
465
+ maxlength="100"
466
+ />
467
+ <div class="tl-text-field__bottom">
468
+ <span class="tl-text-field__helper">Short description</span>
469
+ <span class="tl-text-field__charcounter">
470
+ 0<span class="tl-text-field__charcounter-divider">/</span>100
471
+ </span>
472
+ </div>
473
+ </div>
474
+
475
+ <!-- Disabled -->
476
+ <div class="tl-text-field tl-text-field--lg">
477
+ <label class="tl-text-field__label" for="dis">Disabled</label>
478
+ <input
479
+ class="tl-text-field__input"
480
+ id="dis"
481
+ type="text"
482
+ disabled
483
+ placeholder="Cannot edit"
484
+ />
485
+ </div>
486
+ ```
487
+
488
+ **IMPORTANT for label-inside**: The `placeholder` attribute MUST be set (even to `" "` a space) for the floating label animation to work.
489
+
490
+ ---
491
+
492
+ ## Textarea
493
+
494
+ **Classes**: `tl-textarea`
495
+ **Variants**: `tl-textarea--primary`, `tl-textarea--secondary`, `tl-textarea--label-inside`
496
+ **States**: `tl-textarea--error`, `tl-textarea--success`
497
+
498
+ ```html
499
+ <div class="tl-textarea">
500
+ <label class="tl-textarea__label" for="msg">Message</label>
501
+ <textarea
502
+ class="tl-textarea__input"
503
+ id="msg"
504
+ rows="4"
505
+ placeholder="Type here..."
506
+ ></textarea>
507
+ <div class="tl-textarea__bottom">
508
+ <span class="tl-textarea__helper">Max 500 characters</span>
509
+ <span class="tl-textarea__charcounter">
510
+ 0<span class="tl-textarea__charcounter-divider">/</span>500
511
+ </span>
512
+ </div>
513
+ </div>
514
+ ```
515
+
516
+ ---
517
+
518
+ ## Checkbox
519
+
520
+ **Classes**: `tl-checkbox`
521
+
522
+ ```html
523
+ <label class="tl-checkbox">
524
+ <input class="tl-checkbox__input" type="checkbox" />
525
+ <span class="tl-checkbox__label">Accept terms</span>
526
+ </label>
527
+
528
+ <!-- Checked -->
529
+ <label class="tl-checkbox">
530
+ <input class="tl-checkbox__input" type="checkbox" checked />
531
+ <span class="tl-checkbox__label">Selected</span>
532
+ </label>
533
+
534
+ <!-- Disabled -->
535
+ <label class="tl-checkbox">
536
+ <input class="tl-checkbox__input" type="checkbox" disabled />
537
+ <span class="tl-checkbox__label">Disabled</span>
538
+ </label>
539
+
540
+ <!-- Indeterminate (add class manually) -->
541
+ <label class="tl-checkbox">
542
+ <input
543
+ class="tl-checkbox__input tl-checkbox__input--indeterminate"
544
+ type="checkbox"
545
+ />
546
+ <span class="tl-checkbox__label">Indeterminate</span>
547
+ </label>
548
+ ```
549
+
550
+ ---
551
+
552
+ ## Radio Button
553
+
554
+ **Classes**: `tl-radio-button`
555
+
556
+ ```html
557
+ <label class="tl-radio-button">
558
+ <input class="tl-radio-button__input" type="radio" name="group1" value="a" />
559
+ <span class="tl-radio-button__label">Option A</span>
560
+ </label>
561
+
562
+ <label class="tl-radio-button">
563
+ <input class="tl-radio-button__input" type="radio" name="group1" value="b" />
564
+ <span class="tl-radio-button__label">Option B</span>
565
+ </label>
566
+
567
+ <label class="tl-radio-button">
568
+ <input
569
+ class="tl-radio-button__input"
570
+ type="radio"
571
+ name="group1"
572
+ value="c"
573
+ disabled
574
+ />
575
+ <span class="tl-radio-button__label">Disabled</span>
576
+ </label>
577
+ ```
578
+
579
+ ---
580
+
581
+ ## Toggle / Switch
582
+
583
+ **Classes**: `tl-toggle`
584
+ **Sizes**: default (44×24px), `tl-toggle__input--sm` (28×16px)
585
+
586
+ ```html
587
+ <div class="tl-toggle">
588
+ <span class="tl-toggle__headline">Dark mode</span>
589
+ <label>
590
+ <input class="tl-toggle__input" type="checkbox" />
591
+ <span class="tl-toggle__label">Enable</span>
592
+ </label>
593
+ </div>
594
+
595
+ <!-- Small toggle -->
596
+ <div class="tl-toggle">
597
+ <label>
598
+ <input
599
+ class="tl-toggle__input tl-toggle__input--sm"
600
+ type="checkbox"
601
+ checked
602
+ />
603
+ <span class="tl-toggle__label">On</span>
604
+ </label>
605
+ </div>
606
+ ```
607
+
608
+ ---
609
+
610
+ ## Dropdown / Select
611
+
612
+ **Classes**: `tl-dropdown`
613
+ **Sizes**: default (56px), `tl-dropdown--md` (48px), `tl-dropdown--sm` (40px)
614
+ **Variants**: `tl-dropdown--primary`, `tl-dropdown--secondary`, `tl-dropdown--label-inside`
615
+ **States**: `tl-dropdown--error`
616
+
617
+ ### Native Select
618
+
619
+ ```html
620
+ <div class="tl-dropdown tl-dropdown--lg">
621
+ <label class="tl-dropdown__label" for="country">Country</label>
622
+ <select class="tl-dropdown__select" id="country">
623
+ <option value="">Select a country</option>
624
+ <option value="se">Sweden</option>
625
+ <option value="no">Norway</option>
626
+ <option value="dk">Denmark</option>
627
+ </select>
628
+ </div>
629
+ ```
630
+
631
+ ### Custom Dropdown (requires JS for open/close)
632
+
633
+ ```html
634
+ <div class="tl-dropdown tl-dropdown--lg">
635
+ <label class="tl-dropdown__label">Status</label>
636
+ <button
637
+ class="tl-dropdown__button"
638
+ aria-expanded="false"
639
+ aria-haspopup="listbox"
640
+ >
641
+ <span class="tl-dropdown__text" data-placeholder="Select status"></span>
642
+ </button>
643
+ <ul class="tl-dropdown__list" role="listbox">
644
+ <li class="tl-dropdown__option" role="option" tabindex="0">Active</li>
645
+ <li class="tl-dropdown__option" role="option" tabindex="0">Inactive</li>
646
+ <li class="tl-dropdown__option tl-dropdown__option--disabled" role="option">
647
+ Archived
648
+ </li>
649
+ </ul>
650
+ </div>
651
+ ```
652
+
653
+ ### Filterable Dropdown
654
+
655
+ ```html
656
+ <div class="tl-dropdown tl-dropdown--lg">
657
+ <label class="tl-dropdown__label">Search items</label>
658
+ <div class="tl-dropdown__input-wrapper">
659
+ <input
660
+ class="tl-dropdown__input"
661
+ type="text"
662
+ placeholder="Type to filter..."
663
+ aria-expanded="false"
664
+ role="combobox"
665
+ autocomplete="off"
666
+ />
667
+ <button class="tl-dropdown__input-clear" aria-label="Clear">×</button>
668
+ </div>
669
+ <ul class="tl-dropdown__list" role="listbox">
670
+ <li class="tl-dropdown__option" role="option" tabindex="0">Item 1</li>
671
+ <li class="tl-dropdown__option" role="option" tabindex="0">Item 2</li>
672
+ </ul>
673
+ </div>
674
+ ```
675
+
676
+ ---
677
+
678
+ ## Datetime / Date Picker
679
+
680
+ **Classes**: `tl-datetime`
681
+ **Sizes**: default lg (56px), `tl-datetime--md` (48px), `tl-datetime--sm` (40px)
682
+ **Variants**: `tl-datetime--primary`, `tl-datetime--secondary`, `tl-datetime--label-inside`
683
+ **States**: `tl-datetime--error`
684
+
685
+ ```html
686
+ <div class="tl-datetime tl-datetime--label-inside">
687
+ <div class="tl-datetime__wrapper">
688
+ <span class="tl-datetime__label-inside">Date</span>
689
+ <input class="tl-datetime__input" type="date" />
690
+ </div>
691
+ </div>
692
+
693
+ <!-- With external label -->
694
+ <div class="tl-datetime">
695
+ <label class="tl-datetime__label" for="start">Start date</label>
696
+ <div class="tl-datetime__wrapper">
697
+ <input class="tl-datetime__input" id="start" type="date" />
698
+ </div>
699
+ </div>
700
+
701
+ <!-- Time picker -->
702
+ <div class="tl-datetime">
703
+ <label class="tl-datetime__label" for="time">Time</label>
704
+ <div class="tl-datetime__wrapper">
705
+ <input class="tl-datetime__input" id="time" type="time" />
706
+ </div>
707
+ </div>
708
+
709
+ <!-- Error state -->
710
+ <div class="tl-datetime tl-datetime--error">
711
+ <label class="tl-datetime__label" for="due">Due date</label>
712
+ <div class="tl-datetime__wrapper">
713
+ <input class="tl-datetime__input" id="due" type="date" />
714
+ </div>
715
+ <span class="tl-datetime__helper">Date cannot be in the past</span>
716
+ </div>
717
+ ```
718
+
719
+ ---
720
+
721
+ ## 03-components-display
722
+
723
+ # Display Components
724
+
725
+ ## Card
726
+
727
+ **Classes**: `tl-card`
728
+ **Variants**: `tl-card--primary`, `tl-card--secondary`, `tl-card--clickable`, `tl-card--stretch`, `tl-card--expandable`
729
+
730
+ ```html
731
+ <!-- Basic card -->
732
+ <div class="tl-card">
733
+ <div class="tl-card__header">
734
+ <div class="tl-card__headings">
735
+ <span class="tl-card__title">Card Title</span>
736
+ <span class="tl-card__subtitle">Subtitle</span>
737
+ </div>
738
+ </div>
739
+ <div class="tl-card__body">
740
+ <div class="tl-card__content">
741
+ <p>Card content goes here.</p>
742
+ </div>
743
+ </div>
744
+ </div>
745
+
746
+ <!-- Card with image -->
747
+ <div class="tl-card">
748
+ <img class="tl-card__image" src="image.jpg" alt="Description" />
749
+ <div class="tl-card__header tl-card__header--below">
750
+ <div class="tl-card__headings">
751
+ <span class="tl-card__title">Featured Card</span>
752
+ </div>
753
+ </div>
754
+ <div class="tl-card__body">
755
+ <div class="tl-card__content">
756
+ <p>Content below the image.</p>
757
+ </div>
758
+ </div>
759
+ <div class="tl-card__bottom-row">
760
+ <div class="tl-card__actions">
761
+ <button class="tl-button tl-button--primary tl-button--sm">Action</button>
762
+ </div>
763
+ </div>
764
+ </div>
765
+
766
+ <!-- Clickable card -->
767
+ <button class="tl-card tl-card--clickable">
768
+ <div class="tl-card__header">
769
+ <div class="tl-card__headings">
770
+ <span class="tl-card__title">Clickable</span>
771
+ </div>
772
+ </div>
773
+ <div class="tl-card__body">
774
+ <div class="tl-card__content">Click anywhere</div>
775
+ </div>
776
+ </button>
777
+
778
+ <!-- Card with thumbnail -->
779
+ <div class="tl-card">
780
+ <div class="tl-card__header">
781
+ <img class="tl-card__thumbnail" src="avatar.jpg" alt="User" />
782
+ <div class="tl-card__headings">
783
+ <span class="tl-card__title">User Name</span>
784
+ <span class="tl-card__subtitle">Role</span>
785
+ </div>
786
+ </div>
787
+ </div>
788
+ ```
789
+
790
+ ---
791
+
792
+ ## Accordion
793
+
794
+ **Classes**: `tl-accordion`
795
+ **Variants**: `tl-accordion--primary`, `tl-accordion--secondary`, `tl-accordion--hide-last-border`
796
+
797
+ ```html
798
+ <div class="tl-accordion">
799
+ <!-- Expanded item -->
800
+ <div class="tl-accordion__item tl-accordion__item--expanded">
801
+ <button class="tl-accordion__header-icon-end" aria-expanded="true">
802
+ <span class="tl-accordion__title">Section 1</span>
803
+ </button>
804
+ <div class="tl-accordion__panel">
805
+ <p>Content of section 1.</p>
806
+ </div>
807
+ </div>
808
+
809
+ <!-- Collapsed item -->
810
+ <div class="tl-accordion__item">
811
+ <button class="tl-accordion__header-icon-end" aria-expanded="false">
812
+ <span class="tl-accordion__title">Section 2</span>
813
+ </button>
814
+ <div class="tl-accordion__panel">
815
+ <p>Content of section 2.</p>
816
+ </div>
817
+ </div>
818
+
819
+ <!-- Disabled item -->
820
+ <div class="tl-accordion__item tl-accordion__item--disabled">
821
+ <button
822
+ class="tl-accordion__header-icon-end"
823
+ aria-expanded="false"
824
+ disabled
825
+ >
826
+ <span class="tl-accordion__title">Disabled</span>
827
+ </button>
828
+ <div class="tl-accordion__panel">
829
+ <p>Cannot expand.</p>
830
+ </div>
831
+ </div>
832
+ </div>
833
+ ```
834
+
835
+ **Icon position**: Use `tl-accordion__header-icon-start` for chevron on left, `tl-accordion__header-icon-end` for right.
836
+
837
+ ---
838
+
839
+ ## Badge
840
+
841
+ **Classes**: `tl-badge`
842
+ **Sizes**: `tl-badge--sm` (8×8px dot), `tl-badge--lg` (20×20px), `tl-badge--pill` (auto-width)
843
+
844
+ **IMPORTANT**: The badge component does NOT have built-in status variants (success, warning, danger). It only provides neutral notification badges.
845
+
846
+ ```html
847
+ <!-- Notification badges (default usage) -->
848
+ <span class="tl-badge tl-badge--sm"></span>
849
+ <span class="tl-badge tl-badge--lg"><span class="tl-badge__text">5</span></span>
850
+ <span class="tl-badge tl-badge--pill"
851
+ ><span class="tl-badge__text">New</span></span
852
+ >
853
+ ```
854
+
855
+ ### Status Badges (Custom Component)
856
+
857
+ **NEVER use inline styles on badges.** For status indicators (success, warning, danger), create a custom component with proper CSS classes:
858
+
859
+ ```css
860
+ /* Add to your custom stylesheet */
861
+ .status-badge {
862
+ display: inline-block;
863
+ padding: var(--dimension-space-tiny) var(--dimension-space-small);
864
+ border-radius: var(--dimension-radius-rounded);
865
+ font-size: 12px;
866
+ font-weight: var(--scania-font-weight-bold);
867
+ text-align: center;
868
+ }
869
+
870
+ .status-badge--success {
871
+ background-color: var(--color-system-success-discrete);
872
+ color: var(--color-system-success-default);
873
+ }
874
+
875
+ .status-badge--warning {
876
+ background-color: var(--color-system-warning-discrete);
877
+ color: var(--color-system-warning-default);
878
+ }
879
+
880
+ .status-badge--danger {
881
+ background-color: var(--color-system-danger-discrete);
882
+ color: var(--color-system-danger-default);
883
+ }
884
+
885
+ .status-badge--info {
886
+ background-color: var(--color-system-info-discrete);
887
+ color: var(--color-system-info-default);
888
+ }
889
+ ```
890
+
891
+ ```html
892
+ <!-- CUSTOM: Status badges using Tegel tokens -->
893
+ <span class="status-badge status-badge--success">Active</span>
894
+ <span class="status-badge status-badge--warning">Pending</span>
895
+ <span class="status-badge status-badge--danger">Error</span>
896
+ <span class="status-badge status-badge--info">Info</span>
897
+ ```
898
+
899
+ ---
900
+
901
+ ## Banner
902
+
903
+ **Classes**: `tl-banner`
904
+ **Variants**: `tl-banner--error`, `tl-banner--information`
905
+
906
+ ```html
907
+ <!-- Default banner -->
908
+ <div class="tl-banner" role="alert">
909
+ <span class="tl-banner__icon">
910
+ <span class="tl-icon tl-icon--info"></span>
911
+ </span>
912
+ <div class="tl-banner__content">
913
+ <div class="tl-banner__text">
914
+ <h6 class="tl-banner__header">Notice</h6>
915
+ <p class="tl-banner__subheader">System maintenance scheduled.</p>
916
+ </div>
917
+ <div class="tl-banner__actions">
918
+ <a href="#" class="tl-link tl-link--underline">Learn more</a>
919
+ </div>
920
+ </div>
921
+ <button class="tl-banner__close" aria-label="Close">
922
+ <span class="tl-icon tl-icon--cross"></span>
923
+ </button>
924
+ </div>
925
+
926
+ <!-- Error banner -->
927
+ <div class="tl-banner tl-banner--error" role="alert">
928
+ <span class="tl-banner__icon">
929
+ <span class="tl-icon tl-icon--error"></span>
930
+ </span>
931
+ <div class="tl-banner__content">
932
+ <div class="tl-banner__text">
933
+ <h6 class="tl-banner__header">Error</h6>
934
+ <p class="tl-banner__subheader">Something went wrong.</p>
935
+ </div>
936
+ </div>
937
+ </div>
938
+ ```
939
+
940
+ ---
941
+
942
+ ## Message
943
+
944
+ **Classes**: `tl-message`
945
+ **Variants**: `tl-message--information`, `tl-message--success`, `tl-message--warning`, `tl-message--error`, `tl-message--minimal`
946
+ **Mode**: `tl-message--primary`, `tl-message--secondary`, `tl-message--no-icon`
947
+
948
+ ```html
949
+ <!-- Information message -->
950
+ <div class="tl-message tl-message--information">
951
+ <div class="tl-message__wrapper">
952
+ <div class="tl-message__content">
953
+ <p class="tl-message__header">Information</p>
954
+ <p class="tl-message__extended-message">Here is some helpful context.</p>
955
+ </div>
956
+ </div>
957
+ </div>
958
+
959
+ <!-- Error message -->
960
+ <div class="tl-message tl-message--error">
961
+ <div class="tl-message__wrapper">
962
+ <div class="tl-message__content">
963
+ <p class="tl-message__header">Error occurred</p>
964
+ <p class="tl-message__extended-message">Please try again later.</p>
965
+ </div>
966
+ </div>
967
+ </div>
968
+
969
+ <!-- Minimal (inline) error message -->
970
+ <div class="tl-message tl-message--error tl-message--minimal">
971
+ <div class="tl-message__wrapper">
972
+ <div class="tl-message__content">
973
+ <p class="tl-message__header">Invalid input</p>
974
+ </div>
975
+ </div>
976
+ </div>
977
+ ```
978
+
979
+ ---
980
+
981
+ ## Toast
982
+
983
+ **Classes**: `tl-toast`
984
+ **Variants**: `tl-toast--information`, `tl-toast--success`, `tl-toast--error`, `tl-toast--warning`
985
+
986
+ ```html
987
+ <div class="tl-toast tl-toast--success" role="alert">
988
+ <span class="tl-toast__icon">
989
+ <span class="tl-icon tl-icon--tick"></span>
990
+ </span>
991
+ <div class="tl-toast__content">
992
+ <div class="tl-toast__text">
993
+ <h6 class="tl-toast__header">Success</h6>
994
+ <p class="tl-toast__subheader">Item was saved.</p>
995
+ </div>
996
+ </div>
997
+ <button class="tl-toast__close" aria-label="Dismiss">
998
+ <span class="tl-icon tl-icon--cross"></span>
999
+ </button>
1000
+ </div>
1001
+ ```
1002
+
1003
+ Width is fixed at `348px`. Position with CSS `position: fixed`.
1004
+
1005
+ ---
1006
+
1007
+ ## Modal / Dialog
1008
+
1009
+ **Classes**: `tl-modal`
1010
+ **Sizes**: `tl-modal--xs`, `tl-modal--sm`, `tl-modal--md`, `tl-modal--lg`
1011
+ **Modifiers**: `tl-modal--visible` (shows modal), `tl-modal--sticky` (sticky header/actions)
1012
+
1013
+ ```html
1014
+ <!-- Overlay (backdrop) -->
1015
+ <div class="tl-modal__overlay tl-modal--visible"></div>
1016
+
1017
+ <!-- Modal -->
1018
+ <div
1019
+ class="tl-modal tl-modal--sm tl-modal--visible tl-modal--sticky"
1020
+ role="dialog"
1021
+ aria-modal="true"
1022
+ aria-labelledby="modal-title"
1023
+ >
1024
+ <div class="tl-modal__header">
1025
+ <h5 class="tl-modal__title" id="modal-title">Confirm Action</h5>
1026
+ <button class="tl-modal__close" aria-label="Close">
1027
+ <span class="tl-icon tl-icon--cross"></span>
1028
+ </button>
1029
+ </div>
1030
+ <div class="tl-modal__body">
1031
+ <p>Are you sure you want to proceed?</p>
1032
+ </div>
1033
+ <div class="tl-modal__actions">
1034
+ <button class="tl-button tl-button--secondary tl-button--md">Cancel</button>
1035
+ <button class="tl-button tl-button--primary tl-button--md">Confirm</button>
1036
+ </div>
1037
+ </div>
1038
+ ```
1039
+
1040
+ **Responsive widths** for `tl-modal--sm`:
1041
+
1042
+ - Mobile (320px): 100%
1043
+ - Tablet (672px): 62.5%
1044
+ - Desktop (1056px): 43.75%
1045
+ - Wide (1312px): 37.5%
1046
+ - Ultra-wide (1584px): 37.5%
1047
+
1048
+ ---
1049
+
1050
+ ## Table
1051
+
1052
+ **Classes**: `tl-table`
1053
+ **Variants**: `tl-table--primary`, `tl-table--secondary`, `tl-table--responsive`, `tl-table--compact`
1054
+ **Modifiers**: `tl-table--zebra-rows-odd`, `tl-table--zebra-rows-even`, `tl-table--vertical-dividers`, `tl-table--horizontal-scroll`, `tl-table--clickable`
1055
+
1056
+ ```html
1057
+ <table class="tl-table tl-table--responsive">
1058
+ <!-- Optional toolbar -->
1059
+ <caption class="tl-table__toolbar">
1060
+ <div class="tl-table__upper-bar">
1061
+ <div class="tl-table__actionbar-left">
1062
+ <span class="tl-table__title">Users</span>
1063
+ </div>
1064
+ </div>
1065
+ </caption>
1066
+
1067
+ <thead class="tl-table__header">
1068
+ <tr class="tl-table__row">
1069
+ <th class="tl-table__header-cell">Name</th>
1070
+ <th class="tl-table__header-cell">Email</th>
1071
+ <th class="tl-table__header-cell">Role</th>
1072
+ </tr>
1073
+ </thead>
1074
+
1075
+ <tbody class="tl-table__body">
1076
+ <tr class="tl-table__row">
1077
+ <td class="tl-table__body-cell">John Doe</td>
1078
+ <td class="tl-table__body-cell">john@example.com</td>
1079
+ <td class="tl-table__body-cell">Admin</td>
1080
+ </tr>
1081
+ <tr class="tl-table__row">
1082
+ <td class="tl-table__body-cell">Jane Smith</td>
1083
+ <td class="tl-table__body-cell">jane@example.com</td>
1084
+ <td class="tl-table__body-cell">User</td>
1085
+ </tr>
1086
+ </tbody>
1087
+ </table>
1088
+ ```
1089
+
1090
+ ### Sortable Headers
1091
+
1092
+ ```html
1093
+ <th class="tl-table__header-cell tl-table__header-cell--sortable">
1094
+ <button class="tl-table__header-button">
1095
+ <span class="tl-table__header-button-text">Name</span>
1096
+ <span class="tl-table__header-button-icon tl-icon tl-icon--sorting"></span>
1097
+ </button>
1098
+ </th>
1099
+ ```
1100
+
1101
+ ---
1102
+
1103
+ ## Block (Content Section)
1104
+
1105
+ **Classes**: `tl-block`
1106
+ **Variants**: `tl-block--primary`, `tl-block--secondary`
1107
+
1108
+ ```html
1109
+ <div class="tl-block tl-block--primary">
1110
+ <h2>Section Title</h2>
1111
+ <p>Body text in a block.</p>
1112
+ <div class="tl-block">
1113
+ <h3>Nested Block</h3>
1114
+ <p>Nested content with alternating background.</p>
1115
+ </div>
1116
+ </div>
1117
+ ```
1118
+
1119
+ Blocks alternate background colors automatically when nested.
1120
+
1121
+ ---
1122
+
1123
+ ## Stepper
1124
+
1125
+ **Classes**: `tl-stepper`
1126
+ **Sizes**: `tl-stepper--lg`, `tl-stepper--sm`
1127
+ **Direction**: `tl-stepper--horizontal`, `tl-stepper--vertical`
1128
+ **Label**: `tl-stepper--label-below`, `tl-stepper--label-aside`, `tl-stepper--hide-labels`
1129
+
1130
+ ```html
1131
+ <div class="tl-stepper tl-stepper--horizontal tl-stepper--lg">
1132
+ <ol class="tl-stepper__list">
1133
+ <li class="tl-stepper__step tl-stepper__step--success">
1134
+ <span class="tl-stepper__node"></span>
1135
+ <span class="tl-stepper__label">Step 1</span>
1136
+ </li>
1137
+ <li class="tl-stepper__step tl-stepper__step--current">
1138
+ <span class="tl-stepper__node">2</span>
1139
+ <span class="tl-stepper__label">Step 2</span>
1140
+ </li>
1141
+ <li class="tl-stepper__step tl-stepper__step--upcoming">
1142
+ <span class="tl-stepper__node">3</span>
1143
+ <span class="tl-stepper__label">Step 3</span>
1144
+ </li>
1145
+ </ol>
1146
+ </div>
1147
+ ```
1148
+
1149
+ Step states: `tl-stepper__step--success`, `tl-stepper__step--error`, `tl-stepper__step--current`, `tl-stepper__step--upcoming`
1150
+
1151
+ ---
1152
+
1153
+ ## Spinner / Loader
1154
+
1155
+ **Classes**: `tl-spinner`
1156
+ **Sizes**: `tl-spinner--xs` (16px), `tl-spinner--sm` (24px), `tl-spinner--md` (52px), `tl-spinner--lg` (84px)
1157
+ **Color**: `tl-spinner--default`, `tl-spinner--inverted`
1158
+
1159
+ ```html
1160
+ <svg
1161
+ class="tl-spinner tl-spinner--md tl-spinner--default"
1162
+ aria-label="Loading"
1163
+ role="img"
1164
+ >
1165
+ <circle class="tl-spinner__circle"></circle>
1166
+ </svg>
1167
+ ```
1168
+
1169
+ ---
1170
+
1171
+ ## Popover Canvas
1172
+
1173
+ **Classes**: `tl-popover-canvas`
1174
+ **Modifiers**: `tl-popover-canvas--visible`, `tl-popover-canvas--animation-fade`, `tl-popover-canvas--primary`, `tl-popover-canvas--secondary`
1175
+
1176
+ ```html
1177
+ <div
1178
+ class="tl-popover-canvas tl-popover-canvas--visible tl-popover-canvas--animation-fade"
1179
+ >
1180
+ <div class="tl-popover-canvas__content">
1181
+ <h5 class="tl-popover-canvas__header">Popover Title</h5>
1182
+ <p>Content here.</p>
1183
+ <div class="tl-popover-canvas__actions">
1184
+ <button class="tl-button tl-button--ghost tl-button--sm">Cancel</button>
1185
+ <button class="tl-button tl-button--primary tl-button--sm">
1186
+ Confirm
1187
+ </button>
1188
+ </div>
1189
+ </div>
1190
+ </div>
1191
+ ```
1192
+
1193
+ ---
1194
+
1195
+ ## 04-components-navigation
1196
+
1197
+ # Navigation & Utility Components
1198
+
1199
+ ## Breadcrumbs
1200
+
1201
+ **Classes**: `tl-breadcrumbs`
1202
+
1203
+ ```html
1204
+ <nav class="tl-breadcrumbs" aria-label="Breadcrumb">
1205
+ <ol>
1206
+ <li><a href="/">Home</a></li>
1207
+ <li><a href="/products">Products</a></li>
1208
+ <li><a href="/products/trucks" aria-current="page">Trucks</a></li>
1209
+ </ol>
1210
+ </nav>
1211
+ ```
1212
+
1213
+ ---
1214
+
1215
+ ## Chip
1216
+
1217
+ **Classes**: `tl-chip`
1218
+ **Sizes**: `tl-chip--sm` (24px), `tl-chip--lg` (32px)
1219
+ **States**: `tl-chip--selected`
1220
+ **Modifiers**: `tl-chip--prefix`, `tl-chip--suffix`
1221
+
1222
+ ```html
1223
+ <button class="tl-chip tl-chip--lg">Default</button>
1224
+ <button class="tl-chip tl-chip--lg tl-chip--selected">Selected</button>
1225
+ <button class="tl-chip tl-chip--sm" disabled>Disabled</button>
1226
+ ```
1227
+
1228
+ ---
1229
+
1230
+ ## Tabs
1231
+
1232
+ ### Inline Tabs
1233
+
1234
+ ```html
1235
+ <div class="tl-inline-tabs" role="tablist">
1236
+ <button
1237
+ class="tl-inline-tabs__tab tl-inline-tabs__tab--selected"
1238
+ role="tab"
1239
+ aria-selected="true"
1240
+ >
1241
+ Tab 1
1242
+ </button>
1243
+ <button class="tl-inline-tabs__tab" role="tab" aria-selected="false">
1244
+ Tab 2
1245
+ </button>
1246
+ <button
1247
+ class="tl-inline-tabs__tab tl-inline-tabs__tab--disabled"
1248
+ role="tab"
1249
+ aria-disabled="true"
1250
+ >
1251
+ Disabled
1252
+ </button>
1253
+ </div>
1254
+ ```
1255
+
1256
+ ### Folder Tabs
1257
+
1258
+ ```html
1259
+ <div class="tl-folder-tabs" role="tablist">
1260
+ <button
1261
+ class="tl-folder-tabs__tab tl-folder-tabs__tab--selected"
1262
+ role="tab"
1263
+ aria-selected="true"
1264
+ >
1265
+ Active
1266
+ </button>
1267
+ <button class="tl-folder-tabs__tab" role="tab" aria-selected="false">
1268
+ Another
1269
+ </button>
1270
+ </div>
1271
+ ```
1272
+
1273
+ ### Navigation Tabs
1274
+
1275
+ ```html
1276
+ <nav class="tl-navigation-tabs" role="tablist">
1277
+ <a
1278
+ class="tl-navigation-tabs__tab tl-navigation-tabs__tab--selected"
1279
+ href="/overview"
1280
+ role="tab"
1281
+ aria-selected="true"
1282
+ >Overview</a
1283
+ >
1284
+ <a class="tl-navigation-tabs__tab" href="/details" role="tab">Details</a>
1285
+ </nav>
1286
+ ```
1287
+
1288
+ ---
1289
+
1290
+ ## Link
1291
+
1292
+ **Classes**: `tl-link`
1293
+ **Modifiers**: `tl-link--underline`, `tl-link--standalone`, `tl-link--disabled`
1294
+
1295
+ ```html
1296
+ <a href="/page" class="tl-link tl-link--underline">
1297
+ Learn more
1298
+ <span class="tl-link__icon"
1299
+ ><span class="tl-icon tl-icon--arrow_right"></span
1300
+ ></span>
1301
+ </a>
1302
+
1303
+ <!-- Standalone link (inherits detail-02 typography) -->
1304
+ <a href="/page" class="tl-link tl-link--standalone tl-link--underline"
1305
+ >Standalone Link</a
1306
+ >
1307
+
1308
+ <!-- Disabled link -->
1309
+ <span class="tl-link tl-link--disabled">Disabled link</span>
1310
+ ```
1311
+
1312
+ ---
1313
+
1314
+ ## Divider
1315
+
1316
+ **Classes**: `tl-divider`
1317
+ **Variants**: `tl-divider--horizontal`, `tl-divider--vertical`, `tl-divider--expressive`, `tl-divider--discrete`
1318
+
1319
+ ```html
1320
+ <div class="tl-divider tl-divider--horizontal"></div>
1321
+ <div class="tl-divider tl-divider--vertical" style="height: 40px;"></div>
1322
+ ```
1323
+
1324
+ ---
1325
+
1326
+ ## Tooltip
1327
+
1328
+ **Classes**: `tl-tooltip`
1329
+ **Modifiers**: `tl-tooltip--visible`, position variants: `tl-tooltip--top-start`, `tl-tooltip--top-end`, `tl-tooltip--bottom-start`, `tl-tooltip--bottom-end`, `tl-tooltip--left-start`, `tl-tooltip--left-end`, `tl-tooltip--right-start`, `tl-tooltip--right-end`
1330
+
1331
+ ```html
1332
+ <div class="tl-tooltip tl-tooltip--visible tl-tooltip--bottom-start">
1333
+ Tooltip text here
1334
+ </div>
1335
+ ```
1336
+
1337
+ Max width: 204px. Positioning requires JS or CSS `position: fixed`.
1338
+
1339
+ ---
1340
+
1341
+ ## Popover Menu
1342
+
1343
+ **Classes**: `tl-popover-menu`
1344
+ **Variants**: `tl-popover-menu--primary`, `tl-popover-menu--secondary`, `tl-popover-menu--visible`, `tl-popover-menu--fluid`
1345
+
1346
+ ```html
1347
+ <div
1348
+ class="tl-popover-menu tl-popover-menu--visible tl-popover-menu--animation-fade"
1349
+ >
1350
+ <div class="tl-popover-menu__item-wrapper">
1351
+ <button class="tl-popover-menu__item">
1352
+ <span class="tl-icon tl-icon--edit"></span>
1353
+ Edit
1354
+ </button>
1355
+ <button class="tl-popover-menu__item">
1356
+ <span class="tl-icon tl-icon--copy"></span>
1357
+ Duplicate
1358
+ </button>
1359
+ <div class="tl-divider tl-divider--horizontal"></div>
1360
+ <button class="tl-popover-menu__item--disabled">
1361
+ <span class="tl-icon tl-icon--trash"></span>
1362
+ Delete
1363
+ </button>
1364
+ </div>
1365
+ </div>
1366
+ ```
1367
+
1368
+ ---
1369
+
1370
+ ## Slider
1371
+
1372
+ **Classes**: `tl-slider`
1373
+ **Sizes**: default, `tl-slider--small`
1374
+
1375
+ Slider requires JavaScript for thumb positioning. CSS provides the visual styling only.
1376
+
1377
+ ---
1378
+
1379
+ ## Icon
1380
+
1381
+ **Classes**: `tl-icon tl-icon--{name}`
1382
+ **Sizes**: `tl-icon--12` through `tl-icon--96` (even numbers only)
1383
+
1384
+ Common icons:
1385
+
1386
+ - Navigation: `arrow_left`, `arrow_right`, `arrow_up`, `arrow_down`, `chevron_down`, `chevron_up`, `chevron_left`, `chevron_right`, `back`
1387
+ - Actions: `edit`, `trash`, `copy`, `save`, `download`, `upload`, `export`, `search`, `filters`, `settings`, `plus`, `minus`, `cross`, `tick`
1388
+ - Status: `error`, `warning`, `info`, `tick`
1389
+ - UI: `burger`, `bento`, `kebab`, `meatballs`, `eye`, `eye_inactive`, `lock`, `unlock`
1390
+ - Objects: `calendar`, `clock`, `document`, `folder`, `image`, `profile`, `home`, `notification`
1391
+
1392
+ ```html
1393
+ <span class="tl-icon tl-icon--search"></span>
1394
+ <span class="tl-icon tl-icon--search tl-icon--24"></span>
1395
+ <span class="tl-icon tl-icon--error tl-icon--16"></span>
1396
+ ```
1397
+
1398
+ Icons inherit `color` from parent via `currentcolor`.
1399
+
1400
+ ---
1401
+
1402
+ ## Header
1403
+
1404
+ **Classes**: `tl-header`
1405
+
1406
+ ```html
1407
+ <header class="tl-header">
1408
+ <nav class="tl-header__nav">
1409
+ <ul class="tl-header__list">
1410
+ <!-- Brand logo -->
1411
+ <li class="tl-header__item">
1412
+ <a class="tl-header__item-wrapper" href="/">
1413
+ <span class="tl-header__brand"></span>
1414
+ </a>
1415
+ </li>
1416
+
1417
+ <!-- Title -->
1418
+ <li class="tl-header__item">
1419
+ <div class="tl-header__title">
1420
+ <span class="tl-header__title-text">Application Name</span>
1421
+ </div>
1422
+ </li>
1423
+
1424
+ <!-- Nav items -->
1425
+ <li class="tl-header__item">
1426
+ <a class="tl-header__item-wrapper" href="/dashboard">Dashboard</a>
1427
+ </li>
1428
+
1429
+ <!-- Spacer -->
1430
+ <li class="tl-header__middle-spacer"></li>
1431
+
1432
+ <!-- Dropdown -->
1433
+ <li class="tl-header__dropdown">
1434
+ <button class="tl-header__dropdown-wrapper" aria-expanded="false">
1435
+ <span class="tl-icon tl-icon--profile"></span>
1436
+ </button>
1437
+ <ul class="tl-header__dropdown-menu">
1438
+ <li class="tl-header__dropdown-menu-item">
1439
+ <a href="/settings">Settings</a>
1440
+ </li>
1441
+ <li class="tl-header__dropdown-menu-item">
1442
+ <button>Logout</button>
1443
+ </li>
1444
+ </ul>
1445
+ </li>
1446
+ </ul>
1447
+ </nav>
1448
+ </header>
1449
+ ```
1450
+
1451
+ ---
1452
+
1453
+ ## Footer
1454
+
1455
+ **Classes**: `tl-footer`
1456
+ **Variants**: `tl-footer--primary`, `tl-footer--secondary`
1457
+
1458
+ ```html
1459
+ <footer class="tl-footer">
1460
+ <!-- Top section with link groups -->
1461
+ <div class="tl-footer__top">
1462
+ <div class="tl-footer__group">
1463
+ <button class="tl-footer__top-title">Resources</button>
1464
+ <a class="tl-footer__link" href="#">Documentation</a>
1465
+ <a class="tl-footer__link" href="#">API Reference</a>
1466
+ </div>
1467
+ <div class="tl-footer__group">
1468
+ <button class="tl-footer__top-title">Support</button>
1469
+ <a class="tl-footer__link" href="#">Contact</a>
1470
+ <a class="tl-footer__link" href="#">FAQ</a>
1471
+ </div>
1472
+ </div>
1473
+
1474
+ <!-- Main section with brand -->
1475
+ <div class="tl-footer__main">
1476
+ <div class="tl-footer__main-top">
1477
+ <div class="tl-footer__main-top--start">
1478
+ <div class="tl-footer__group">
1479
+ <a class="tl-footer__link" href="#">Privacy Policy</a>
1480
+ <a class="tl-footer__link" href="#">Cookie Policy</a>
1481
+ </div>
1482
+ </div>
1483
+ </div>
1484
+ <div class="tl-footer__main-bottom">
1485
+ <p class="tl-footer__copyright">© 2025 Scania CV AB</p>
1486
+ <span class="tl-footer__brand">Scania</span>
1487
+ </div>
1488
+ </div>
1489
+ </footer>
1490
+ ```
1491
+
1492
+ ---
1493
+
1494
+ ## Side Menu
1495
+
1496
+ **Classes**: `tl-side-menu`
1497
+ **Modifiers**: `tl-side-menu--persistent`, `tl-side-menu--collapsed`
1498
+
1499
+ ```html
1500
+ <nav class="tl-side-menu tl-side-menu--persistent" aria-label="Side navigation">
1501
+ <div class="tl-side-menu__wrapper tl-side-menu__wrapper--open">
1502
+ <div class="tl-side-menu__overlay"></div>
1503
+ <aside class="tl-side-menu__aside">
1504
+ <div class="tl-side-menu__navigation">
1505
+ <div class="tl-side-menu__list-wrapper">
1506
+ <ul class="tl-side-menu__upper-list">
1507
+ <li>
1508
+ <a
1509
+ class="tl-side-menu__item tl-side-menu__item--selected"
1510
+ href="/dashboard"
1511
+ >
1512
+ <span class="tl-icon tl-icon--dashboard"></span>
1513
+ Dashboard
1514
+ </a>
1515
+ </li>
1516
+ <li>
1517
+ <!-- Dropdown -->
1518
+ <div class="tl-side-menu__dropdown">
1519
+ <button class="tl-side-menu__item">
1520
+ <span class="tl-icon tl-icon--settings"></span>
1521
+ Settings
1522
+ </button>
1523
+ <div class="tl-side-menu__dropdown-menu">
1524
+ <ul class="tl-side-menu__dropdown-list">
1525
+ <li>
1526
+ <a
1527
+ class="tl-side-menu__dropdown-item"
1528
+ href="/settings/general"
1529
+ >General</a
1530
+ >
1531
+ </li>
1532
+ <li>
1533
+ <a
1534
+ class="tl-side-menu__dropdown-item"
1535
+ href="/settings/account"
1536
+ >Account</a
1537
+ >
1538
+ </li>
1539
+ </ul>
1540
+ </div>
1541
+ </div>
1542
+ </li>
1543
+ </ul>
1544
+ </div>
1545
+ </div>
1546
+ </aside>
1547
+ </div>
1548
+ </nav>
1549
+ ```
1550
+
1551
+ ---
1552
+
1553
+ ## 05-guidelines
1554
+
1555
+ # Guidelines
1556
+
1557
+ ## Spacing & Sizing Rules
1558
+
1559
+ ### Spacing Custom Properties (available globally)
1560
+
1561
+ These are used by components internally and can be used for custom layouts:
1562
+
1563
+ - `--tds-spacing-element-4` (4px)
1564
+ - `--tds-spacing-element-8` (8px)
1565
+ - `--tds-spacing-element-12` (12px)
1566
+ - `--tds-spacing-element-16` (16px)
1567
+ - `--tds-spacing-element-20` (20px)
1568
+ - `--tds-spacing-element-24` (24px)
1569
+ - `--tds-spacing-element-32` (32px)
1570
+ - `--tds-spacing-element-48` (48px)
1571
+ - `--tds-spacing-layout-64` (64px)
1572
+
1573
+ ### Rules
1574
+
1575
+ 1. Use `var(--dimension-space-small)` (16px) as the base gap between elements.
1576
+ 2. Use `var(--dimension-space-medium)` (24px) for section padding.
1577
+ 3. Use `var(--dimension-space-large)` (48px) for major section separators.
1578
+ 4. Component height follows the sizing convention: sm=40px, md=48px, lg=56px.
1579
+
1580
+ ---
1581
+
1582
+ ## Color Usage
1583
+
1584
+ ### Semantic Color Usage
1585
+
1586
+ | Purpose | Token to Use |
1587
+ | ------------------------- | --------------------------------------------- |
1588
+ | Page background | `var(--color-background-base)` |
1589
+ | Card/section background | `var(--color-background-layer-01)` |
1590
+ | Nested section background | `var(--color-background-layer-02)` |
1591
+ | Primary heading text | `var(--color-foreground-text-strong)` |
1592
+ | Secondary text | `var(--color-foreground-text-defined)` |
1593
+ | Subtle/hint text | `var(--color-foreground-text-subtle)` |
1594
+ | Link text | `var(--color-foreground-text-accent-default)` |
1595
+ | Disabled text | `var(--color-foreground-text-disabled)` |
1596
+ | Default border | `var(--color-foreground-border-soft)` |
1597
+ | Focus outline | `var(--color-foreground-border-accent-focus)` |
1598
+ | Success state | `var(--color-system-success-default)` |
1599
+ | Warning state | `var(--color-system-warning-default)` |
1600
+ | Error/danger state | `var(--color-system-danger-default)` |
1601
+ | Info state | `var(--color-system-info-default)` |
1602
+
1603
+ ### RULE: Never hardcode colors. Always use CSS custom properties.
1604
+
1605
+ ---
1606
+
1607
+ ## Responsiveness
1608
+
1609
+ ### Breakpoints
1610
+
1611
+ | Name | Width | CSS |
1612
+ | ------- | -------- | ---------------------------- |
1613
+ | Mobile | < 672px | `@media (max-width: 671px)` |
1614
+ | Tablet | ≥ 672px | `@media (min-width: 672px)` |
1615
+ | Desktop | ≥ 992px | `@media (min-width: 992px)` |
1616
+ | Wide | ≥ 1056px | `@media (min-width: 1056px)` |
1617
+ | X-Wide | ≥ 1312px | `@media (min-width: 1312px)` |
1618
+ | XX-Wide | ≥ 1584px | `@media (min-width: 1584px)` |
1619
+
1620
+ ### Rules
1621
+
1622
+ 1. **Mobile-first**: Write base styles for mobile, add media queries for larger screens.
1623
+ 2. Header nav items hide below 992px (burger menu shows).
1624
+ 3. Side menu collapses to overlay below 992px.
1625
+ 4. Footer top section stacks vertically below 992px.
1626
+ 5. Modal widths are responsive (100% on mobile, percentage-based on desktop).
1627
+
1628
+ ---
1629
+
1630
+ ## Accessibility (a11y)
1631
+
1632
+ ### Required ARIA Attributes
1633
+
1634
+ | Component | Required ARIA |
1635
+ | ------------- | ----------------------------------------------------------------------------- |
1636
+ | Button | `aria-label` for icon-only buttons |
1637
+ | Modal | `role="dialog"`, `aria-modal="true"`, `aria-labelledby` |
1638
+ | Accordion | `aria-expanded="true/false"` on trigger |
1639
+ | Tabs | `role="tablist"`, `role="tab"`, `aria-selected` |
1640
+ | Dropdown | `aria-expanded`, `aria-haspopup="listbox"`, `role="listbox"`, `role="option"` |
1641
+ | Breadcrumbs | `aria-label="Breadcrumb"`, `aria-current="page"` on last item |
1642
+ | Toast/Banner | `role="alert"` |
1643
+ | Tooltip | `role="tooltip"`, `aria-describedby` on trigger |
1644
+ | Spinner | `aria-label="Loading"`, `role="img"` |
1645
+ | Close buttons | `aria-label="Close"` or `aria-label="Dismiss"` |
1646
+ | Navigation | `aria-label` on `<nav>` elements |
1647
+
1648
+ ### Focus Management
1649
+
1650
+ - All interactive elements have `:focus-visible` styles (2px solid blue-400 outline).
1651
+ - Focus outline uses `var(--color-foreground-border-accent-focus)`.
1652
+ - Modals should trap focus.
1653
+ - Side menu overlay closes on Escape.
1654
+
1655
+ ### Contrast
1656
+
1657
+ - Use semantic tokens (they meet WCAG 2.1 AA contrast requirements).
1658
+ - Never override text colors with lighter alternatives.
1659
+ - `--color-foreground-text-strong` on `--color-background-base` = ≥4.5:1 ratio.
1660
+
1661
+ ---
1662
+
1663
+ ## Code Conventions
1664
+
1665
+ ### BEM Naming Pattern
1666
+
1667
+ ```
1668
+ .tl-{block} → Component root
1669
+ .tl-{block}__{element} → Sub-part of component
1670
+ .tl-{block}--{modifier} → Variant/state
1671
+ .tl-{block}__{element}--{mod} → Element variant
1672
+ ```
1673
+
1674
+ ### Class Order in HTML
1675
+
1676
+ 1. Base class first: `tl-button`
1677
+ 2. Variant modifier: `tl-button--primary`
1678
+ 3. Size modifier: `tl-button--md`
1679
+ 4. State modifier: `tl-button--full-width`
1680
+
1681
+ Example: `class="tl-button tl-button--primary tl-button--md tl-button--full-width"`
1682
+
1683
+ ### Rules
1684
+
1685
+ 1. **NEVER write custom CSS** if a Tegel Lite class already exists for the purpose.
1686
+ 2. **NEVER use inline styles for colors, spacing, or any design properties.** ALWAYS use CSS classes with Tegel tokens.
1687
+ - ❌ BAD: `<span style="background-color: var(--color-system-success-discrete);">Active</span>`
1688
+ - ✅ GOOD: Create a CSS class `.status-badge--success { background-color: var(--color-system-success-discrete); }`
1689
+ 3. **NEVER add inline styles to Tegel components.** If a component doesn't have the variant you need, create a custom component.
1690
+ 4. **ALWAYS use semantic HTML** (`<button>`, `<a>`, `<nav>`, `<header>`, etc.).
1691
+ 5. **ALWAYS wrap the app** in `<body class="scania tds-mode-light">`.
1692
+ 6. When building custom components, use Tegel tokens for all colors, spacing, and typography.
1693
+ 7. Keep custom CSS in separate files, never modify the Tegel Lite CSS files.
1694
+
1695
+ ---
1696
+
1697
+ ## 06-patterns
1698
+
1699
+ # Composed Page Patterns
1700
+
1701
+ ## Login Page
1702
+
1703
+ ```html
1704
+ <body class="scania tds-mode-light">
1705
+ <div
1706
+ style="display: flex; align-items: center; justify-content: center; min-height: 100vh; background-color: var(--color-background-base);"
1707
+ >
1708
+ <div class="tl-card" style="width: 400px; max-width: 100%;">
1709
+ <div class="tl-card__header">
1710
+ <div class="tl-card__headings">
1711
+ <span class="tl-card__title">Sign In</span>
1712
+ <span class="tl-card__subtitle">Enter your credentials</span>
1713
+ </div>
1714
+ </div>
1715
+ <div class="tl-card__body">
1716
+ <div class="tl-card__content" style="max-inline-size: unset;">
1717
+ <!-- Email field -->
1718
+ <div
1719
+ class="tl-text-field tl-text-field--lg"
1720
+ style="margin-bottom: 16px;"
1721
+ >
1722
+ <label class="tl-text-field__label" for="login-email">Email</label>
1723
+ <input
1724
+ class="tl-text-field__input"
1725
+ id="login-email"
1726
+ type="email"
1727
+ placeholder="user@scania.com"
1728
+ />
1729
+ </div>
1730
+ <!-- Password field -->
1731
+ <div
1732
+ class="tl-text-field tl-text-field--lg"
1733
+ style="margin-bottom: 24px;"
1734
+ >
1735
+ <label class="tl-text-field__label" for="login-pass"
1736
+ >Password</label
1737
+ >
1738
+ <input
1739
+ class="tl-text-field__input"
1740
+ id="login-pass"
1741
+ type="password"
1742
+ placeholder="Enter password"
1743
+ />
1744
+ </div>
1745
+ <button
1746
+ class="tl-button tl-button--primary tl-button--md tl-button--full-width"
1747
+ >
1748
+ Sign In
1749
+ </button>
1750
+ </div>
1751
+ </div>
1752
+ </div>
1753
+ </div>
1754
+ </body>
1755
+ ```
1756
+
1757
+ ---
1758
+
1759
+ ## Dashboard Layout (Header + Sidebar + Content)
1760
+
1761
+ ```html
1762
+ <body class="scania tds-mode-light">
1763
+ <!-- Header -->
1764
+ <header class="tl-header">
1765
+ <nav class="tl-header__nav">
1766
+ <ul class="tl-header__list">
1767
+ <li class="tl-header__item">
1768
+ <a class="tl-header__item-wrapper" href="/"
1769
+ ><span class="tl-header__brand"></span
1770
+ ></a>
1771
+ </li>
1772
+ <li class="tl-header__item">
1773
+ <div class="tl-header__title">
1774
+ <span class="tl-header__title-text">Fleet Manager</span>
1775
+ </div>
1776
+ </li>
1777
+ <li class="tl-header__middle-spacer"></li>
1778
+ <li class="tl-header__item">
1779
+ <button class="tl-header__item-wrapper" aria-label="Notifications">
1780
+ <span class="tl-icon tl-icon--notification"></span>
1781
+ </button>
1782
+ </li>
1783
+ </ul>
1784
+ </nav>
1785
+ </header>
1786
+
1787
+ <!-- Layout -->
1788
+ <div style="display: flex; min-height: calc(100vh - 64px);">
1789
+ <!-- Side Menu -->
1790
+ <nav
1791
+ class="tl-side-menu tl-side-menu--persistent"
1792
+ aria-label="Main navigation"
1793
+ style="position: static; height: auto;"
1794
+ >
1795
+ <div class="tl-side-menu__wrapper tl-side-menu__wrapper--open">
1796
+ <aside class="tl-side-menu__aside">
1797
+ <div class="tl-side-menu__navigation">
1798
+ <div class="tl-side-menu__list-wrapper">
1799
+ <ul class="tl-side-menu__upper-list">
1800
+ <li>
1801
+ <a
1802
+ class="tl-side-menu__item tl-side-menu__item--selected"
1803
+ href="/dashboard"
1804
+ >
1805
+ <span class="tl-icon tl-icon--dashboard"></span> Dashboard
1806
+ </a>
1807
+ </li>
1808
+ <li>
1809
+ <a class="tl-side-menu__item" href="/vehicles">
1810
+ <span class="tl-icon tl-icon--truck"></span> Vehicles
1811
+ </a>
1812
+ </li>
1813
+ </ul>
1814
+ </div>
1815
+ </div>
1816
+ </aside>
1817
+ </div>
1818
+ </nav>
1819
+
1820
+ <!-- Main Content -->
1821
+ <main
1822
+ style="flex: 1; padding: var(--dimension-space-medium); background-color: var(--color-background-base);"
1823
+ >
1824
+ <!-- Cards grid -->
1825
+ <div
1826
+ style="display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 16px; margin-bottom: 24px;"
1827
+ >
1828
+ <div class="tl-card">
1829
+ <div class="tl-card__header">
1830
+ <div class="tl-card__headings">
1831
+ <span class="tl-card__title">Total Vehicles</span>
1832
+ </div>
1833
+ </div>
1834
+ <div class="tl-card__body">
1835
+ <div
1836
+ class="tl-card__content"
1837
+ style="max-inline-size: unset; font-size: 32px; font-weight: 700;"
1838
+ >
1839
+ 142
1840
+ </div>
1841
+ </div>
1842
+ </div>
1843
+ </div>
1844
+
1845
+ <!-- Table -->
1846
+ <table class="tl-table tl-table--responsive">
1847
+ <thead class="tl-table__header">
1848
+ <tr class="tl-table__row">
1849
+ <th class="tl-table__header-cell">Vehicle</th>
1850
+ <th class="tl-table__header-cell">Status</th>
1851
+ <th class="tl-table__header-cell">Location</th>
1852
+ </tr>
1853
+ </thead>
1854
+ <tbody class="tl-table__body">
1855
+ <tr class="tl-table__row">
1856
+ <td class="tl-table__body-cell">SCN-2024-001</td>
1857
+ <td class="tl-table__body-cell">Active</td>
1858
+ <td class="tl-table__body-cell">Södertälje</td>
1859
+ </tr>
1860
+ </tbody>
1861
+ </table>
1862
+ </main>
1863
+ </div>
1864
+ </body>
1865
+ ```
1866
+
1867
+ ---
1868
+
1869
+ ## Form Page
1870
+
1871
+ ```html
1872
+ <main
1873
+ style="max-width: 600px; margin: 0 auto; padding: var(--dimension-space-medium);"
1874
+ >
1875
+ <h2
1876
+ style="font-family: var(--scania-font-family-default); font-weight: var(--scania-font-weight-bold); margin-bottom: 24px;"
1877
+ >
1878
+ New Vehicle Registration
1879
+ </h2>
1880
+
1881
+ <form>
1882
+ <div style="display: flex; flex-direction: column; gap: 16px;">
1883
+ <div class="tl-text-field tl-text-field--lg">
1884
+ <label class="tl-text-field__label" for="vin">VIN Number</label>
1885
+ <input
1886
+ class="tl-text-field__input"
1887
+ id="vin"
1888
+ type="text"
1889
+ placeholder="Enter VIN"
1890
+ />
1891
+ </div>
1892
+
1893
+ <div class="tl-dropdown tl-dropdown--lg">
1894
+ <label class="tl-dropdown__label" for="type">Vehicle Type</label>
1895
+ <select class="tl-dropdown__select" id="type">
1896
+ <option value="">Select type</option>
1897
+ <option value="truck">Truck</option>
1898
+ <option value="bus">Bus</option>
1899
+ </select>
1900
+ </div>
1901
+
1902
+ <div class="tl-datetime">
1903
+ <label class="tl-datetime__label" for="reg-date"
1904
+ >Registration Date</label
1905
+ >
1906
+ <div class="tl-datetime__wrapper">
1907
+ <input class="tl-datetime__input" id="reg-date" type="date" />
1908
+ </div>
1909
+ </div>
1910
+
1911
+ <div class="tl-textarea">
1912
+ <label class="tl-textarea__label" for="notes">Notes</label>
1913
+ <textarea
1914
+ class="tl-textarea__input"
1915
+ id="notes"
1916
+ rows="4"
1917
+ placeholder="Additional details..."
1918
+ ></textarea>
1919
+ </div>
1920
+
1921
+ <label class="tl-checkbox">
1922
+ <input class="tl-checkbox__input" type="checkbox" />
1923
+ <span class="tl-checkbox__label"
1924
+ >I confirm all information is correct</span
1925
+ >
1926
+ </label>
1927
+
1928
+ <div style="display: flex; gap: 16px; padding-top: 8px;">
1929
+ <button
1930
+ type="button"
1931
+ class="tl-button tl-button--secondary tl-button--md"
1932
+ >
1933
+ Cancel
1934
+ </button>
1935
+ <button
1936
+ type="submit"
1937
+ class="tl-button tl-button--primary tl-button--md"
1938
+ >
1939
+ Register
1940
+ </button>
1941
+ </div>
1942
+ </div>
1943
+ </form>
1944
+ </main>
1945
+ ```
1946
+
1947
+ ---
1948
+
1949
+ # AI Assistant Prompt Rules
1950
+
1951
+ ## CRITICAL: NO INLINE STYLES
1952
+
1953
+ **NEVER EVER use inline `style=""` attributes for design properties on ANY element, especially not on Tegel components.**
1954
+
1955
+ This includes:
1956
+
1957
+ - ❌ Colors: `style="color: ..."` or `style="background-color: ..."`
1958
+ - ❌ Spacing: `style="padding: ..."` or `style="margin: ..."`
1959
+ - ❌ Typography: `style="font-size: ..."` or `style="font-weight: ..."`
1960
+ - ❌ Any design tokens: `style="background-color: var(--color-system-success-discrete);"`
1961
+
1962
+ **ALWAYS create CSS classes instead:**
1963
+
1964
+ - ✅ Define class in `<style>` section with Tegel tokens
1965
+ - ✅ Use BEM naming: `.custom-component--variant`
1966
+ - ✅ Mark with comment: `<!-- CUSTOM: Description -->`
1967
+
1968
+ **If a Tegel component doesn't have the variant you need (e.g., status badges), create a custom component with proper CSS classes, NOT inline styles.**
1969
+
1970
+ ---
1971
+
1972
+ ## Component Generation
1973
+
1974
+ 1. When the user asks for a button, ALWAYS default to `tl-button tl-button--md` (variant is optional).
1975
+ 2. When the user asks for a button with icon:
1976
+ - ALWAYS add `tl-button--icon` modifier
1977
+ - ALWAYS put text BEFORE the icon
1978
+ - ALWAYS add `aria-hidden="true"` to the icon
1979
+ - Example: `<button class="tl-button tl-button--icon tl-button--md">Save<span class="tl-icon tl-icon--save" aria-hidden="true"></span></button>`
1980
+ 3. When the user asks for an icon-only button:
1981
+ - ALWAYS add `tl-button--only-icon` modifier
1982
+ - ALWAYS add `aria-label` attribute describing the action
1983
+ - Example: `<button class="tl-button tl-button--only-icon tl-button--md" aria-label="Settings"><span class="tl-icon tl-icon--settings"></span></button>`
1984
+ 4. When the user asks for an input/text field, ALWAYS default to `tl-text-field tl-text-field--lg` with an external label.
1985
+ 5. When the user asks for a card, ALWAYS include `tl-card__header` and `tl-card__body` sections.
1986
+ 6. When the user asks for a table, ALWAYS include `tl-table__header` with `th.tl-table__header-cell` and `tl-table__body`.
1987
+
1988
+ ## Code Quality
1989
+
1990
+ 7. Generate ALWAYS complete, production-ready HTML – never pseudocode.
1991
+ 8. Generate ALWAYS all necessary wrapper elements (e.g., `tl-card__body` > `tl-card__content`).
1992
+ 9. Include ALWAYS relevant ARIA attributes for accessibility:
1993
+ - Decorative icons in buttons with text: `aria-hidden="true"`
1994
+ - Icon-only buttons: `aria-label="Action description"`
1995
+ - Form labels: proper `for` and `id` attributes
1996
+ 10. Comment HTML with `<!-- -->` to explain structure sections.
1997
+ 11. Use ALWAYS the `scania tds-mode-light` classes on the `<body>` or root element.
1998
+
1999
+ ## Token Usage
2000
+
2001
+ 12. Use NEVER hardcoded color values – ALWAYS CSS custom properties.
2002
+ 13. Use NEVER hardcoded pixel spacing – prefer Tegel spacing tokens.
2003
+ 14. Use NEVER inline styles for any design properties (color, background, spacing, typography).
2004
+ - ❌ `style="color: blue"` or `style="color: var(--color-foreground-text-accent-default)"`
2005
+ - ✅ Create CSS class: `.custom-text { color: var(--color-foreground-text-accent-default); }`
2006
+ 15. Use NEVER `style="background: #f5f5f5"` – create a CSS class instead.
2007
+ 16. If a Tegel component lacks a needed variant, create a CUSTOM component with BEM classes, not inline styles.
2008
+
2009
+ ## Custom Components
2010
+
2011
+ 17. If the user requests a component that doesn't exist in Tegel Lite, build it using existing tokens and classes, and clearly mark it with a comment: `<!-- CUSTOM: Not a native Tegel Lite component -->`.
2012
+ 18. Custom components should follow the BEM naming convention: `.tl-custom-{name}__element--modifier`.
2013
+
2014
+ ## File Structure
2015
+
2016
+ 19. Always import CSS files in the correct order: variables → global → components.
2017
+ 20. Never modify the Tegel Lite source CSS files.
2018
+ 21. Place custom overrides in a separate stylesheet loaded AFTER the Tegel files.
2019
+
2020
+ ## Dark Mode
2021
+
2022
+ 22. To switch to dark mode, replace `tds-mode-light` with `tds-mode-dark` on the wrapper element.
2023
+ 23. All components automatically adapt – no additional work needed.
2024
+ 24. When showing dark mode examples, add `tds-mode-dark` to the root wrapper.
2025
+
2026
+ ## Icon Usage
2027
+
2028
+ 25. Icons use the pattern `tl-icon tl-icon--{name}` and optionally `tl-icon--{size}`.
2029
+ 26. Icons inherit color from parent. Use `color: var(--color-foreground-icon-strong)` on parent if needed.
2030
+ 27. Decorative icons in buttons with text MUST have `aria-hidden="true"`.
2031
+ 28. Icon-only interactive elements MUST have `aria-label` describing the action.
2032
+
2033
+ ## Size Convention
2034
+
2035
+ 29. Component sizes follow this convention (unless stated otherwise):
2036
+ - `--xs` = 24px height (buttons only)
2037
+ - `--sm` = 40px height
2038
+ - `--md` = 48px height
2039
+ - `--lg` = 56px height (default for inputs)
2040
+
2041
+ ## Mode Variants
2042
+
2043
+ 30. Components with `--primary` use `var(--color-background-layer-01)`.
2044
+ 31. Components with `--secondary` use `var(--color-background-layer-02)`.
2045
+ 32. Variant modifiers (primary, secondary, ghost, danger) are optional for buttons – use based on context.