@gostudent/shared-ui-library-mcp 1.23.2-DT-16968

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,1569 @@
1
+ {
2
+ "libraryVersion": "1.23.2",
3
+ "components": [
4
+ {
5
+ "name": "Avatar",
6
+ "description": "The **Avatar** component displays a user's photo or, when no image is available, their\ninitials on a colour background. The background colour is derived deterministically from\nthe user's name so the same person always gets the same colour.\n\nThree sizes are available — `sm` (32px), `md` (40px), `lg` (48px) — and four colour\nvariants: `pink`, `purple`, `green`, `orange`.\n\n### Usage\n\n```tsx\nimport { Avatar } from '@gostudent/shared-ui-library';\n\n<Avatar name=\"Jane Doe\" />\n<Avatar name=\"Jane Doe\" src=\"/path/to/photo.jpg\" />\n<Avatar name=\"Jane Doe\" status=\"done\" />\n```\n\n### Accessibility\n\n- Renders a `role=\"img\"` span with `aria-label` set to the user's name.\n- The initials text is `aria-hidden` to avoid redundant screen reader output.\n- The status badge is `aria-hidden` as it is decorative.",
7
+ "props": [
8
+ {
9
+ "name": "name",
10
+ "description": "Full name used for initials fallback and aria-label.",
11
+ "options": []
12
+ },
13
+ {
14
+ "name": "src",
15
+ "description": "Image URL. Falls back to initials on load error.",
16
+ "options": []
17
+ },
18
+ {
19
+ "name": "size",
20
+ "description": "Avatar diameter: sm=32px, md=40px, lg=48px.",
21
+ "options": [
22
+ "sm",
23
+ "md",
24
+ "lg"
25
+ ]
26
+ },
27
+ {
28
+ "name": "color",
29
+ "description": "Background colour for the initials variant. Omit to derive from name.",
30
+ "options": [
31
+ "pink",
32
+ "purple",
33
+ "green",
34
+ "orange"
35
+ ]
36
+ },
37
+ {
38
+ "name": "status",
39
+ "description": "When \"done",
40
+ "options": [
41
+ "default",
42
+ "done"
43
+ ]
44
+ },
45
+ {
46
+ "name": "className",
47
+ "description": "Additional CSS class applied to the root element.",
48
+ "options": []
49
+ }
50
+ ],
51
+ "examples": [
52
+ {
53
+ "name": "Default",
54
+ "jsx": "<Avatar name=\"Jane Doe\" size=\"md\" status=\"default\" />"
55
+ }
56
+ ]
57
+ },
58
+ {
59
+ "name": "AvatarGroup",
60
+ "description": "The **AvatarGroup** component renders a stack of overlapping `Avatar` components.\nWhen `max` is set, additional avatars are collapsed into a `+N` overflow badge.\n\n### Usage\n\n```tsx\nimport { AvatarGroup, Avatar } from '@gostudent/shared-ui-library';\n\n<AvatarGroup max={3}>\n <Avatar name=\"Anna Smith\" />\n <Avatar name=\"Ben Johnson\" />\n <Avatar name=\"Clara Williams\" />\n <Avatar name=\"Dan Brown\" />\n</AvatarGroup>\n```",
61
+ "props": [
62
+ {
63
+ "name": "max",
64
+ "description": "Maximum avatars to show before collapsing into +N overflow.",
65
+ "options": []
66
+ },
67
+ {
68
+ "name": "overlap",
69
+ "description": "Pixel overlap between avatars. Default: 9.",
70
+ "options": []
71
+ }
72
+ ],
73
+ "examples": [
74
+ {
75
+ "name": "Default",
76
+ "jsx": "<AvatarGroup overlap={9} />"
77
+ }
78
+ ]
79
+ },
80
+ {
81
+ "name": "Button",
82
+ "description": "The **Button** component is a flexible, accessible button that supports three variants\n(`primary`, `secondary`, `link`), six sizes (`xxs` → `xl`), optional leading/trailing icons,\nand full disabled and focus states.\n\nColours are driven by CSS custom properties so the button automatically adapts to the\nactive brand theme (`gs` or `sk`). Switch themes using the toolbar at the top of the page.\n\n### Usage\n\n```tsx\nimport { Button } from '@gostudent/shared-ui-library';\n\n<Button variant=\"primary\" size=\"md\" onClick={handleClick}>\n Save changes\n</Button>\n```\n\n### Accessibility\n\n- Renders a native `<button>` element — keyboard and screen-reader accessible by default.\n- Use `type=\"submit\"` inside a `<form>` to trigger form submission.\n- Disabled buttons have `cursor: not-allowed` and are excluded from tab order.\n- Focus ring is visible and uses `outline-offset` to avoid overlapping the border.",
83
+ "props": [
84
+ {
85
+ "name": "variant",
86
+ "description": "Visual style of the button.",
87
+ "options": [
88
+ "primary",
89
+ "secondary",
90
+ "link"
91
+ ]
92
+ },
93
+ {
94
+ "name": "size",
95
+ "description": "Height and font-size scale. Accepts a single size or a `[mobile, desktop]",
96
+ "options": [
97
+ "xxs",
98
+ "xs",
99
+ "sm",
100
+ "md",
101
+ "lg",
102
+ "xl"
103
+ ]
104
+ },
105
+ {
106
+ "name": "disabled",
107
+ "description": "When true the button is non-interactive.",
108
+ "options": []
109
+ },
110
+ {
111
+ "name": "forceFocus",
112
+ "description": "Force the focus-visible ring appearance.",
113
+ "options": []
114
+ },
115
+ {
116
+ "name": "forceActive",
117
+ "description": "Force the active/pressed appearance.",
118
+ "options": []
119
+ },
120
+ {
121
+ "name": "children",
122
+ "description": "Button label text.",
123
+ "options": []
124
+ },
125
+ {
126
+ "name": "leftIcon",
127
+ "description": "React node rendered to the left of the label.",
128
+ "options": []
129
+ },
130
+ {
131
+ "name": "rightIcon",
132
+ "description": "React node rendered to the right of the label.",
133
+ "options": []
134
+ }
135
+ ],
136
+ "examples": [
137
+ {
138
+ "name": "Default",
139
+ "jsx": "<Button variant=\"primary\" size=\"md\" children=\"Button\" disabled={false} />"
140
+ },
141
+ {
142
+ "name": "Primary",
143
+ "jsx": "<Button variant=\"primary\" size=\"md\" children=\"Button\" />"
144
+ },
145
+ {
146
+ "name": "Secondary",
147
+ "jsx": "<Button variant=\"secondary\" size=\"md\" children=\"Button\" />"
148
+ }
149
+ ]
150
+ },
151
+ {
152
+ "name": "Calendar",
153
+ "description": "Single-month date picker built on react-day-picker. Controlled only — consumer manages `value` and `onChange`.\n\n### Peer dependencies\n\nThis component requires **`date-fns`** to be installed in the consuming project:\n\n```sh\nnpm install date-fns\n```\n\n### Locale\n\nPass a `date-fns` `Locale` object via the `locale` prop. Resolve it from your app's i18n state — the component does not accept a locale string.\n\n```ts\n// i18next (e.g. tutor-portal)\nimport { de, enGB } from 'date-fns/locale';\nconst locale = i18n.language.startsWith('de') ? de : enGB;\n\n// Next.js App Router (e.g. gs-desk-frontend)\nimport { de, enGB } from 'date-fns/locale';\nconst locale = lang === 'de' ? de : enGB;\n```\n\nWhen `locale` is omitted, the calendar defaults to `enGB`.",
154
+ "props": [
155
+ {
156
+ "name": "value",
157
+ "description": "Currently selected date. Pass `null",
158
+ "options": []
159
+ },
160
+ {
161
+ "name": "onChange",
162
+ "description": "Called with a noon-normalised `Date",
163
+ "options": []
164
+ },
165
+ {
166
+ "name": "markedDates",
167
+ "description": "Dates that show a dot indicator (e.g. days with booked lessons).",
168
+ "options": []
169
+ },
170
+ {
171
+ "name": "disabledDates",
172
+ "description": "Dates that are not selectable.",
173
+ "options": []
174
+ },
175
+ {
176
+ "name": "locale",
177
+ "description": "A `date-fns",
178
+ "options": []
179
+ },
180
+ {
181
+ "name": "weekStartsOn",
182
+ "description": "0 = week starts Sunday, 1 = week starts Monday (default).",
183
+ "options": []
184
+ },
185
+ {
186
+ "name": "renderDayContent",
187
+ "description": "Render prop called with the selected date. Return content shown below the calendar grid. Nothing is rendered when `value",
188
+ "options": []
189
+ },
190
+ {
191
+ "name": "markedDatesLabel",
192
+ "description": "Legend label for marked dates. Defaults to \"has lesson\".",
193
+ "options": []
194
+ },
195
+ {
196
+ "name": "secondaryMarkedDates",
197
+ "description": "Dates that show a red dot indicator (e.g. days with pending feedback).",
198
+ "options": []
199
+ },
200
+ {
201
+ "name": "secondaryMarkedDatesLabel",
202
+ "description": "Legend label for secondary marked dates. Defaults to \"has pending feedback\".",
203
+ "options": []
204
+ },
205
+ {
206
+ "name": "todayLabel",
207
+ "description": "Label for the Today button. Defaults to \"Today\".",
208
+ "options": []
209
+ },
210
+ {
211
+ "name": "previousMonthLabel",
212
+ "description": "Accessible label for the previous month nav button. Defaults to \"Previous month\".",
213
+ "options": []
214
+ },
215
+ {
216
+ "name": "nextMonthLabel",
217
+ "description": "Accessible label for the next month nav button. Defaults to \"Next month\".",
218
+ "options": []
219
+ }
220
+ ],
221
+ "examples": [
222
+ {
223
+ "name": "Default",
224
+ "jsx": "// See \"Default\" story — uses interactive render wrapper"
225
+ },
226
+ {
227
+ "name": "WithMarkedDates",
228
+ "jsx": "// See \"WithMarkedDates\" story — uses interactive render wrapper"
229
+ },
230
+ {
231
+ "name": "WithBothLegends",
232
+ "jsx": "// See \"WithBothLegends\" story — uses interactive render wrapper"
233
+ }
234
+ ]
235
+ },
236
+ {
237
+ "name": "Card",
238
+ "description": "The **Card** component is a styled container providing background, border, border-radius,\nand padding. It sets `container-type: inline-size` so child components can use CSS\ncontainer queries for responsive behaviour.\n\n### Usage\n\n```tsx\nimport { Card } from '@gostudent/shared-ui-library';\n\n<Card padding=\"lg\" radius=\"sm\">\n <p>Card content</p>\n</Card>\n```",
239
+ "props": [
240
+ {
241
+ "name": "padding",
242
+ "description": "Inner padding: none | sm=8px | md=16px | lg=24px.",
243
+ "options": [
244
+ "none",
245
+ "sm",
246
+ "md",
247
+ "lg"
248
+ ]
249
+ },
250
+ {
251
+ "name": "radius",
252
+ "description": "Border radius: sm=8px | md=12px | lg=16px.",
253
+ "options": [
254
+ "sm",
255
+ "md",
256
+ "lg"
257
+ ]
258
+ },
259
+ {
260
+ "name": "bordered",
261
+ "description": "Show a 1px border using the default border colour.",
262
+ "options": []
263
+ }
264
+ ],
265
+ "examples": [
266
+ {
267
+ "name": "Default",
268
+ "jsx": "<Card padding=\"lg\" radius=\"sm\" bordered={true} children=\"Card content goes here.\" />"
269
+ },
270
+ {
271
+ "name": "NoBorder",
272
+ "jsx": "<Card padding=\"lg\" radius=\"sm\" bordered={false} children=\"This card has no border.\" />"
273
+ }
274
+ ]
275
+ },
276
+ {
277
+ "name": "Checkbox",
278
+ "description": "A styled checkbox input with an optional label. Wraps a native `<input type=\"checkbox\">` so all standard input props (checked, onChange, disabled, etc.) work out of the box.\n\nTypography is responsive: body-md on mobile, body-sm on desktop (tablet breakpoint).\n\n### Accessibility\n\nThe component renders as a `<label>` wrapping a visually-hidden native checkbox, preserving full keyboard and screen-reader support. Focus ring is displayed on `:focus-visible` only.\n\n### Usage\n\n```tsx\nimport { Checkbox } from '@gostudent/shared-ui-library';\n\n<Checkbox\n label=\"Pending feedback\"\n checked={value}\n onChange={(e) => setValue(e.target.checked)}\n/>\n```",
279
+ "props": [
280
+ {
281
+ "name": "label",
282
+ "description": "Text or React node rendered next to the checkbox box.",
283
+ "options": []
284
+ },
285
+ {
286
+ "name": "checked",
287
+ "description": "Whether the checkbox is checked (controlled).",
288
+ "options": []
289
+ },
290
+ {
291
+ "name": "disabled",
292
+ "description": "When true, the checkbox is non-interactive and visually muted.",
293
+ "options": []
294
+ },
295
+ {
296
+ "name": "onChange",
297
+ "description": "Change handler — receives the native `React.ChangeEvent<HTMLInputElement>`.",
298
+ "options": []
299
+ }
300
+ ],
301
+ "examples": [
302
+ {
303
+ "name": "Default",
304
+ "jsx": "<Checkbox label=\"Option label\" />"
305
+ },
306
+ {
307
+ "name": "Checked",
308
+ "jsx": "<Checkbox label=\"Option label\" checked={true} onChange={() => {} />"
309
+ },
310
+ {
311
+ "name": "Disabled",
312
+ "jsx": "<Checkbox label=\"Disabled unchecked\" disabled={true} />"
313
+ }
314
+ ]
315
+ },
316
+ {
317
+ "name": "DataList",
318
+ "description": "A structured list for displaying tabular-style data rows under a titled section. Supports an optional add action button and shows an empty state when no items are present.\n\nEach row renders four fixed fields — date, event type, subject, and grade — with an optional `actions` slot for icons such as edit or delete.\n\n### Usage\n\n```tsx\nimport { DataList } from '@gostudent/shared-ui-library';\n\n<DataList\n title=\"Grades (optional)\"\n items={[{ id: '1', date: '22.01.26', eventType: 'Exam', subject: 'English', grade: '2+' }]}\n onAdd={() => {}}\n/>\n```",
319
+ "props": [
320
+ {
321
+ "name": "title",
322
+ "description": "Section label rendered above the list.",
323
+ "options": []
324
+ },
325
+ {
326
+ "name": "items",
327
+ "description": "Array of data rows. Each item requires `id",
328
+ "options": []
329
+ },
330
+ {
331
+ "name": "addLabel",
332
+ "description": "Label for the add action button. Defaults to \"Add grade\".",
333
+ "options": []
334
+ },
335
+ {
336
+ "name": "onAdd",
337
+ "description": "Callback fired when the add button is clicked. When omitted, the button is not rendered.",
338
+ "options": []
339
+ },
340
+ {
341
+ "name": "emptyStateText",
342
+ "description": "Placeholder text shown inside the empty state container when `items",
343
+ "options": []
344
+ },
345
+ {
346
+ "name": "className",
347
+ "description": "Additional CSS class applied to the root element.",
348
+ "options": []
349
+ }
350
+ ],
351
+ "examples": [
352
+ {
353
+ "name": "Default",
354
+ "jsx": "<DataList title=\"Grades (optional)\" items={sampleItems} onAdd={() => {} />"
355
+ },
356
+ {
357
+ "name": "EmptyState",
358
+ "jsx": "<DataList title=\"Grades (optional)\" items={[]} onAdd={() => {} />"
359
+ },
360
+ {
361
+ "name": "WithoutAddButton",
362
+ "jsx": "<DataList title=\"Grades (optional)\" items={sampleItems} />"
363
+ }
364
+ ]
365
+ },
366
+ {
367
+ "name": "Disclosure",
368
+ "description": "A lightweight disclosure (expand/collapse) component that reveals inline content when toggled.\n\nFollows the WAI-ARIA disclosure pattern — the trigger is a `<button>` with `aria-expanded`,\nand the collapsible region appears directly below, pushing layout down (not a floating overlay).\n\nSupports both **uncontrolled** (via `defaultOpen`) and **controlled** (via `open` + `onToggle`) modes.\n\n### Usage\n\n```tsx\nimport { Disclosure, InfoIcon } from '@gostudent/shared-ui-library';\n\n<Disclosure\n trigger={\n <>\n <InfoIcon size=\"sm\" />\n <span>5 pending feedback</span>\n </>\n }\n>\n <ul>\n <li>Alice — awaiting review</li>\n <li>Bob — submitted</li>\n </ul>\n</Disclosure>\n```",
369
+ "props": [
370
+ {
371
+ "name": "trigger",
372
+ "description": "Content rendered inside the toggle button (icon + label, etc.).",
373
+ "options": []
374
+ },
375
+ {
376
+ "name": "children",
377
+ "description": "Collapsible content revealed when the disclosure is open.",
378
+ "options": []
379
+ },
380
+ {
381
+ "name": "defaultOpen",
382
+ "description": "Initial open state in uncontrolled mode. Ignored when `open",
383
+ "options": []
384
+ },
385
+ {
386
+ "name": "open",
387
+ "description": "Controlled open state. When provided the component is fully controlled.",
388
+ "options": []
389
+ },
390
+ {
391
+ "name": "onToggle",
392
+ "description": "Callback fired with the next open state whenever the trigger is clicked.",
393
+ "options": []
394
+ },
395
+ {
396
+ "name": "className",
397
+ "description": "Additional CSS class applied to the root element.",
398
+ "options": []
399
+ }
400
+ ],
401
+ "examples": [
402
+ {
403
+ "name": "Default",
404
+ "jsx": "<Disclosure trigger={( <> <InfoIcon size=\"sm\" /> <span>5 pending feedback</span> </> )} children={( <ul style={{ margin: '8px 0 0'} padding=\"0 0 0 1.25rem\" listStyle=\"disc' }}> <li>Alice — awaiting review</li> <li>Bob — submitted</li> <li>Charlie — in progress</li> <li>Diana — not started</li> <li>Eve — awaiting review</li> </ul> ) defaultOpen={false} />"
405
+ },
406
+ {
407
+ "name": "DefaultOpen",
408
+ "jsx": "<Disclosure trigger={( <> <InfoIcon size=\"sm\" /> <span>5 pending feedback</span> </> )} children={( <ul style={{ margin: '8px 0 0'} padding=\"0 0 0 1.25rem\" listStyle=\"disc' }}> <li>Alice — awaiting review</li> <li>Bob — submitted</li> <li>Charlie — in progress</li> <li>Diana — not started</li> <li>Eve — awaiting review</li> </ul> ) defaultOpen={true} />"
409
+ },
410
+ {
411
+ "name": "TextOnlyTrigger",
412
+ "jsx": "<Disclosure trigger={<span>Show details</span>} children={( <p style={{ margin: '8px 0 0'} fontSize=\"14px' }}> Any React content can go here — lists />"
413
+ }
414
+ ]
415
+ },
416
+ {
417
+ "name": "Divider",
418
+ "description": "",
419
+ "props": [
420
+ {
421
+ "name": "color",
422
+ "description": "Override the border color. Accepts any CSS color value or variable, e.g. `var(--color-border-default)`.",
423
+ "options": []
424
+ }
425
+ ],
426
+ "examples": [
427
+ {
428
+ "name": "Default",
429
+ "jsx": "<Divider spacingTop=\"sm\" spacingBottom=\"sm\" />"
430
+ }
431
+ ]
432
+ },
433
+ {
434
+ "name": "Drawer",
435
+ "description": "Generic slide-in panel with optional header, scrollable body, and footer slots.\n\nSupports four **positions**: `left`, `right`, `top`, `bottom`.\n\n**overlay behaviour:**\n- `overlay={true}` (default) — drawer slides **over** page content; a semi-transparent scrim dims the background. Click the scrim or press Escape to close.\n- `overlay={false}` — drawer **pushes** page content sideways; no scrim, background stays fully visible and interactive.",
436
+ "props": [
437
+ {
438
+ "name": "isOpen",
439
+ "description": "",
440
+ "options": []
441
+ },
442
+ {
443
+ "name": "onClose",
444
+ "description": "",
445
+ "options": []
446
+ },
447
+ {
448
+ "name": "position",
449
+ "description": "",
450
+ "options": []
451
+ },
452
+ {
453
+ "name": "width",
454
+ "description": "",
455
+ "options": []
456
+ },
457
+ {
458
+ "name": "height",
459
+ "description": "",
460
+ "options": []
461
+ },
462
+ {
463
+ "name": "header",
464
+ "description": "",
465
+ "options": []
466
+ },
467
+ {
468
+ "name": "footer",
469
+ "description": "",
470
+ "options": []
471
+ },
472
+ {
473
+ "name": "children",
474
+ "description": "",
475
+ "options": []
476
+ },
477
+ {
478
+ "name": "overlay",
479
+ "description": "",
480
+ "options": []
481
+ },
482
+ {
483
+ "name": "fullScreenOnMobile",
484
+ "description": "",
485
+ "options": []
486
+ },
487
+ {
488
+ "name": "scrollable",
489
+ "description": "",
490
+ "options": []
491
+ },
492
+ {
493
+ "name": "className",
494
+ "description": "",
495
+ "options": []
496
+ },
497
+ {
498
+ "name": "aria-label",
499
+ "description": "",
500
+ "options": []
501
+ }
502
+ ],
503
+ "examples": [
504
+ {
505
+ "name": "Default",
506
+ "jsx": "// See \"Default\" story — uses interactive render wrapper"
507
+ },
508
+ {
509
+ "name": "RightSide",
510
+ "jsx": "// See \"RightSide\" story — uses interactive render wrapper"
511
+ },
512
+ {
513
+ "name": "Top",
514
+ "jsx": "// See \"Top\" story — uses interactive render wrapper"
515
+ }
516
+ ]
517
+ },
518
+ {
519
+ "name": "DropdownMenu",
520
+ "description": "**DropdownMenu** is a styled menu panel used with an **IconButton** trigger.\nIt is a presentational component — the consumer manages open/closed state and positioning.\n\n### Usage\n\n```tsx\nimport { DropdownMenu, DropdownMenuItem, IconButton, KebabIcon } from '@gostudent/shared-ui-library';\n\nconst [open, setOpen] = useState(false);\n\n<div style={{ position: 'relative', display: 'inline-block' }}>\n <IconButton icon={<KebabIcon />} aria-label=\"More options\" onClick={() => setOpen(o => !o)} />\n {open && (\n <div style={{ position: 'absolute', top: '100%', right: 0, marginTop: 4 }}>\n <DropdownMenu>\n <DropdownMenuItem onClick={() => {}}>Reschedule lesson</DropdownMenuItem>\n <DropdownMenuItem onClick={() => {}}>Cancel lesson</DropdownMenuItem>\n </DropdownMenu>\n </div>\n )}\n</div>\n```",
521
+ "props": [
522
+ {
523
+ "name": "children",
524
+ "description": "",
525
+ "options": []
526
+ },
527
+ {
528
+ "name": "className",
529
+ "description": "",
530
+ "options": []
531
+ }
532
+ ],
533
+ "examples": [
534
+ {
535
+ "name": "Default",
536
+ "jsx": "// See \"Default\" story — uses interactive render wrapper"
537
+ },
538
+ {
539
+ "name": "WithoutIcons",
540
+ "jsx": "// See \"WithoutIcons\" story — uses interactive render wrapper"
541
+ },
542
+ {
543
+ "name": "WithDisabledItem",
544
+ "jsx": "// See \"WithDisabledItem\" story — uses interactive render wrapper"
545
+ }
546
+ ]
547
+ },
548
+ {
549
+ "name": "Grid",
550
+ "description": "The **Grid** component is a layout primitive that wraps CSS Grid. It maps `columns` and `gap` values to the design token spacing scale and supports responsive tuples for both.\n\n### Usage\n\n```tsx\nimport { Grid } from '@gostudent/shared-ui-library';\n\n<Grid columns={3} gap=\"md\">\n <div>A</div>\n <div>B</div>\n <div>C</div>\n</Grid>\n```\n\n### Responsive — 2-tuple `[mobile, desktop]`\n\n```tsx\n<Grid columns={[1, 3]} gap={['sm', 'md']}>\n ...\n</Grid>\n```\n\n### Responsive — 3-tuple `[mobile, tablet, desktop]`\n\nMatches the Figma grid spec: 4 columns on mobile, 8 on tablet (≥ 640 px), 12 on desktop (≥ 1024 px).\n\n```tsx\n<Grid columns={[4, 8, 12]} gap={['xs', 'sm', 'md']}>\n ...\n</Grid>\n```",
551
+ "props": [
552
+ {
553
+ "name": "columns",
554
+ "description": "Number of equal-width columns. Accepts a single value, a `[mobile, desktop]",
555
+ "options": []
556
+ },
557
+ {
558
+ "name": "gap",
559
+ "description": "Gap between cells, mapped to `--spacing-*",
560
+ "options": [
561
+ "none",
562
+ "3xs",
563
+ "xxs",
564
+ "xs",
565
+ "sm",
566
+ "md",
567
+ "lg",
568
+ "xl",
569
+ "2xl",
570
+ "3xl"
571
+ ]
572
+ },
573
+ {
574
+ "name": "rowGap",
575
+ "description": "Separate row gap override. Falls back to `gap",
576
+ "options": [
577
+ "none",
578
+ "3xs",
579
+ "xxs",
580
+ "xs",
581
+ "sm",
582
+ "md",
583
+ "lg",
584
+ "xl",
585
+ "2xl",
586
+ "3xl"
587
+ ]
588
+ },
589
+ {
590
+ "name": "align",
591
+ "description": "Aligns grid items along the block axis (`align-items`). Defaults to `stretch`.",
592
+ "options": [
593
+ "start",
594
+ "center",
595
+ "end",
596
+ "stretch"
597
+ ]
598
+ },
599
+ {
600
+ "name": "as",
601
+ "description": "Renders as this HTML element or React component. Defaults to `div`.",
602
+ "options": []
603
+ },
604
+ {
605
+ "name": "className",
606
+ "description": "Optional extra CSS class for layout overrides.",
607
+ "options": []
608
+ }
609
+ ],
610
+ "examples": [
611
+ {
612
+ "name": "Default",
613
+ "jsx": "<Grid columns={3} gap=\"md\" />"
614
+ },
615
+ {
616
+ "name": "Responsive",
617
+ "jsx": "<Grid mobileColumns={4} tabletColumns={8} desktopColumns={12} mobileGap=\"xs\" tabletGap=\"sm\" desktopGap=\"md\" />"
618
+ }
619
+ ]
620
+ },
621
+ {
622
+ "name": "IconButton",
623
+ "description": "The **IconButton** is an accessible, icon-only button. Because it has no visible label,\n`aria-label` is required.\n\n### Usage\n\n```tsx\nimport { IconButton } from '@gostudent/shared-ui-library';\n\n<IconButton icon={<KebabIcon />} aria-label=\"More options\" />\n```\n\n### Accessibility\n\n- Renders a native `<button>` with a required `aria-label`.\n- Has a visible focus ring via `focus-visible`.\n- Disabled state reduces opacity to 40 % and sets `cursor: not-allowed`.",
624
+ "props": [
625
+ {
626
+ "name": "size",
627
+ "description": "Button diameter: sm=24px, md=32px, lg=40px.",
628
+ "options": [
629
+ "sm",
630
+ "md",
631
+ "lg"
632
+ ]
633
+ },
634
+ {
635
+ "name": "disabled",
636
+ "description": "When true the button is non-interactive.",
637
+ "options": []
638
+ },
639
+ {
640
+ "name": "color",
641
+ "description": "Override the icon colour with any CSS color value or token variable (e.g. `var(--color-feedback-negative-icon)`).",
642
+ "options": []
643
+ }
644
+ ],
645
+ "examples": [
646
+ {
647
+ "name": "Default",
648
+ "jsx": "<IconButton icon={<KebabIcon />} size=\"md\" disabled={false} />"
649
+ },
650
+ {
651
+ "name": "Disabled",
652
+ "jsx": "<IconButton icon={<KebabIcon />} size=\"md\" disabled={true} />"
653
+ }
654
+ ]
655
+ },
656
+ {
657
+ "name": "InputSelect",
658
+ "description": "Single-select input with a dropdown list.\n\n- Trigger styled as an input field (48 px height, 2 px border, 4 px radius)\n- Chevron rotates 180° when open\n- Selected option shows a checkmark and a distinct background\n- Optional left icon slot (e.g. `GlobeIcon` for language pickers)\n- Closes on **Escape** or **click outside**\n- Full keyboard + ARIA support: `role=\"listbox\"` / `role=\"option\"` / `aria-selected`",
659
+ "props": [
660
+ {
661
+ "name": "options",
662
+ "description": "",
663
+ "options": []
664
+ },
665
+ {
666
+ "name": "value",
667
+ "description": "",
668
+ "options": []
669
+ },
670
+ {
671
+ "name": "onChange",
672
+ "description": "",
673
+ "options": []
674
+ },
675
+ {
676
+ "name": "placeholder",
677
+ "description": "",
678
+ "options": []
679
+ },
680
+ {
681
+ "name": "icon",
682
+ "description": "",
683
+ "options": []
684
+ },
685
+ {
686
+ "name": "disabled",
687
+ "description": "",
688
+ "options": []
689
+ },
690
+ {
691
+ "name": "className",
692
+ "description": "",
693
+ "options": []
694
+ },
695
+ {
696
+ "name": "aria-label",
697
+ "description": "",
698
+ "options": []
699
+ },
700
+ {
701
+ "name": "error",
702
+ "description": "",
703
+ "options": []
704
+ }
705
+ ],
706
+ "examples": [
707
+ {
708
+ "name": "Default",
709
+ "jsx": "// See \"Default\" story — uses interactive render wrapper"
710
+ },
711
+ {
712
+ "name": "WithSelection",
713
+ "jsx": "// See \"WithSelection\" story — uses interactive render wrapper"
714
+ },
715
+ {
716
+ "name": "WithIcon",
717
+ "jsx": "// See \"WithIcon\" story — uses interactive render wrapper"
718
+ }
719
+ ]
720
+ },
721
+ {
722
+ "name": "InputText",
723
+ "description": "A single-line text input field with an optional label, placeholder, tooltip slot, and error message.\n\nSupports all standard HTML `<input>` attributes (type, name, value, onChange, etc.) in addition to its own props.\n\n### Usage\n\n```tsx\nimport { InputText } from '@gostudent/shared-ui-library';\n\n<InputText\n label=\"Full name (required)\"\n placeholder=\"Your first and last names\"\n onChange={(e) => console.log(e.target.value)}\n/>\n\n<InputText\n label=\"Full name (required)\"\n error=\"Please enter your name\"\n/>\n```",
724
+ "props": [
725
+ {
726
+ "name": "label",
727
+ "description": "Label text rendered above the input field.",
728
+ "options": []
729
+ },
730
+ {
731
+ "name": "placeholder",
732
+ "description": "Placeholder text shown when the input is empty.",
733
+ "options": []
734
+ },
735
+ {
736
+ "name": "error",
737
+ "description": "Error message rendered below the input. Also applies the error border style.",
738
+ "options": []
739
+ },
740
+ {
741
+ "name": "disabled",
742
+ "description": "When true, the input is non-interactive. The background and border switch to disabled tokens; text content fades to 40% opacity.",
743
+ "options": []
744
+ },
745
+ {
746
+ "name": "tooltip",
747
+ "description": "React node rendered in the 20×20 slot to the right of the label. Intended for a help/info icon.",
748
+ "options": []
749
+ },
750
+ {
751
+ "name": "size",
752
+ "description": "Text size of the input. `md",
753
+ "options": [
754
+ "md",
755
+ "lg"
756
+ ]
757
+ },
758
+ {
759
+ "name": "multiline",
760
+ "description": "When true, renders a `<textarea>",
761
+ "options": []
762
+ }
763
+ ],
764
+ "examples": [
765
+ {
766
+ "name": "Default",
767
+ "jsx": "<InputText label=\"Full name (required)\" placeholder=\"Your first and last names\" />"
768
+ },
769
+ {
770
+ "name": "Error",
771
+ "jsx": "<InputText label=\"Full name (required)\" placeholder=\"Your first and last names\" error=\"Please enter your name\" />"
772
+ },
773
+ {
774
+ "name": "Disabled",
775
+ "jsx": "<InputText label=\"Full name (required)\" placeholder=\"Input text\" disabled={true} />"
776
+ }
777
+ ]
778
+ },
779
+ {
780
+ "name": "MetadataItem",
781
+ "description": "The **MetadataItem** component pairs a small icon with a text label for displaying\nstructured information such as dates, times, durations, or participant counts.\n\n### Usage\n\n```tsx\nimport { MetadataItem, EventNoteIcon } from '@gostudent/shared-ui-library';\n\n<MetadataItem icon={<EventNoteIcon />}>Mon, 14 Apr 2025</MetadataItem>\n```",
782
+ "props": [
783
+ {
784
+ "name": "children",
785
+ "description": "Text content displayed next to the icon.",
786
+ "options": []
787
+ }
788
+ ],
789
+ "examples": [
790
+ {
791
+ "name": "Default",
792
+ "jsx": "<MetadataItem icon={<EventNoteIcon />} children=\"Mon />"
793
+ }
794
+ ]
795
+ },
796
+ {
797
+ "name": "Modal",
798
+ "description": "A primitive overlay dialog. Handles the portal, scrim, focus trap, scroll lock, size variants, and keyboard/click dismissal.\n\nAll content is composed via `children`. Use `labelledBy` to wire up `aria-labelledby` by pointing it at the `id` of your heading element inside children.\n\n### Usage\n\n```tsx\nimport { Modal } from '@gostudent/shared-ui-library';\n\nfunction ConfirmModal({ isOpen, onClose }) {\n return (\n <Modal isOpen={isOpen} onClose={onClose} labelledBy=\"confirm-title\">\n <h2 id=\"confirm-title\">Confirm action</h2>\n <p>This cannot be undone.</p>\n <div style={{ display: 'flex', gap: 8 }}>\n <Button variant=\"secondary\" size=\"md\" onClick={onClose}>Cancel</Button>\n <Button variant=\"primary\" size=\"md\" onClick={handleConfirm}>Confirm</Button>\n </div>\n </Modal>\n );\n}\n```",
799
+ "props": [
800
+ {
801
+ "name": "isOpen",
802
+ "description": "",
803
+ "options": []
804
+ },
805
+ {
806
+ "name": "onClose",
807
+ "description": "",
808
+ "options": []
809
+ },
810
+ {
811
+ "name": "labelledBy",
812
+ "description": "",
813
+ "options": []
814
+ },
815
+ {
816
+ "name": "dismissable",
817
+ "description": "",
818
+ "options": []
819
+ },
820
+ {
821
+ "name": "size",
822
+ "description": "",
823
+ "options": []
824
+ },
825
+ {
826
+ "name": "fullScreenMobile",
827
+ "description": "",
828
+ "options": []
829
+ },
830
+ {
831
+ "name": "noPadding",
832
+ "description": "",
833
+ "options": []
834
+ },
835
+ {
836
+ "name": "children",
837
+ "description": "",
838
+ "options": []
839
+ },
840
+ {
841
+ "name": "className",
842
+ "description": "",
843
+ "options": []
844
+ },
845
+ {
846
+ "name": "data-testid",
847
+ "description": "",
848
+ "options": []
849
+ }
850
+ ],
851
+ "examples": [
852
+ {
853
+ "name": "Default",
854
+ "jsx": "<Modal size=\"sm\" dismissable={true} fullScreenMobile={false} />"
855
+ },
856
+ {
857
+ "name": "PrimaryActionOnly",
858
+ "jsx": "<Modal size=\"sm\" dismissable={true} />"
859
+ },
860
+ {
861
+ "name": "NoButtons",
862
+ "jsx": "<Modal size=\"sm\" dismissable={true} />"
863
+ }
864
+ ]
865
+ },
866
+ {
867
+ "name": "InlineNotification",
868
+ "description": "Embedded within the page layout, directly associated with a specific area of content.\nStays visible until the user dismisses it or the condition resolves.\n\nSupports five intent types: `info-neutral`, `info-branded`, `success`, `warning`, `error`.\n\nThe layout is responsive: stacked (mobile) → horizontal single-row (desktop ≥ 1024px).\nUse `stacked` to force the column layout regardless of viewport — useful inside narrow sidebar columns.\n\n### Usage\n\n```tsx\nimport { InlineNotification } from '@gostudent/shared-ui-library';\n\n<InlineNotification\n type=\"success\"\n title=\"Changes saved\"\n message=\"Your profile has been updated successfully.\"\n onDismiss={() => {}}\n/>\n```",
869
+ "props": [
870
+ {
871
+ "name": "type",
872
+ "description": "",
873
+ "options": [
874
+ "info-neutral",
875
+ "info-branded",
876
+ "success",
877
+ "warning",
878
+ "error"
879
+ ]
880
+ }
881
+ ],
882
+ "examples": [
883
+ {
884
+ "name": "Default",
885
+ "jsx": "<InlineNotification type=\"info-neutral\" title=\"Notification title\" message=\"This is an informational message.\" />"
886
+ },
887
+ {
888
+ "name": "InfoNeutral",
889
+ "jsx": "<InlineNotification type=\"info-neutral\" title=\"Notification title\" message=\"This is an informational message.\" actionLink={true} />"
890
+ },
891
+ {
892
+ "name": "InfoBranded",
893
+ "jsx": "<InlineNotification type=\"info-branded\" title=\"Notification title\" message=\"This is an informational message.\" actionLink={true} />"
894
+ }
895
+ ]
896
+ },
897
+ {
898
+ "name": "ToastNotification",
899
+ "description": "Transient overlay notifications that appear over the page content and auto-dismiss after a set duration (default 5s).\n\nWrap your app (or story) in `<ToastProvider>` and trigger toasts via the `useToast` hook.\n\n### Interaction rules\n- Hovering a toast pauses the auto-dismiss timer\n- Clicking ✕ dismisses immediately\n- New toasts push existing ones down (max 3 visible at once)\n- On mobile: full-width bottom-sheet style\n\n### Usage\n\n```tsx\nimport { ToastProvider, useToast } from '@gostudent/shared-ui-library';\n\nfunction App() {\n return (\n <ToastProvider>\n <MyPage />\n </ToastProvider>\n );\n}\n\nfunction MyPage() {\n const { addToast } = useToast();\n return (\n <button onClick={() => addToast({ type: 'success', title: 'Saved', message: 'Your changes have been saved.' })}>\n Save\n </button>\n );\n}\n```",
900
+ "props": [
901
+ {
902
+ "name": "children",
903
+ "description": "",
904
+ "options": []
905
+ }
906
+ ],
907
+ "examples": [
908
+ {
909
+ "name": "Interactive",
910
+ "jsx": "// See \"Interactive\" story — uses interactive render wrapper"
911
+ },
912
+ {
913
+ "name": "InteractionRules",
914
+ "jsx": "// See \"InteractionRules\" story — uses interactive render wrapper"
915
+ }
916
+ ]
917
+ },
918
+ {
919
+ "name": "TopBanner",
920
+ "description": "Full-width persistent notification placed above or below the main navigation bar.\nUsed for high-priority, global announcements (maintenance windows, subscription warnings, system-wide alerts).\n\n- Inline single-line layout, 56px height\n- 5 intent types: `info-neutral`, `info-branded`, `success`, `warning`, `error`\n- `info-branded` uses the Stars icon and branded colours\n- Max 1 banner at a time (usage rule — not enforced by the component)\n- Does not auto-dismiss\n\n### Usage\n\n```tsx\nimport { TopBanner } from '@gostudent/shared-ui-library';\n\n<TopBanner\n type=\"warning\"\n title=\"Scheduled maintenance\"\n message=\"Maintenance on Apr 26, 02:00–04:00 UTC.\"\n actionLink\n linkText=\"Learn more\"\n onDismiss={() => {}}\n/>\n```",
921
+ "props": [
922
+ {
923
+ "name": "type",
924
+ "description": "",
925
+ "options": [
926
+ "info-neutral",
927
+ "info-branded",
928
+ "success",
929
+ "warning",
930
+ "error"
931
+ ]
932
+ }
933
+ ],
934
+ "examples": [
935
+ {
936
+ "name": "Default",
937
+ "jsx": "<TopBanner type=\"info-neutral\" title=\"Information title\" message=\"This is an informational message.\" actionLink={true} linkText=\"Action link\" />"
938
+ },
939
+ {
940
+ "name": "InfoBranded",
941
+ "jsx": "<TopBanner type=\"info-branded\" title=\"Information title\" message=\"This is an informational message.\" actionLink={true} linkText=\"Action link\" />"
942
+ },
943
+ {
944
+ "name": "Success",
945
+ "jsx": "<TopBanner type=\"success\" title=\"Information title\" message=\"This is an informational message.\" actionLink={true} />"
946
+ }
947
+ ]
948
+ },
949
+ {
950
+ "name": "SelectionControl",
951
+ "description": "**SelectionControl** is a controlled button for selectable options. Content is fully\nagnostic — pass anything as `children`. Consumer manages `state` and `onClick`.\n\nIt is designed to cover two usage patterns:\n- **Form-value collection** — consumer holds the selected value in state and sends it to an API (e.g. attendance, mood rating, student pills).\n- **Action / navigation** — click triggers rendering new content or navigating (e.g. tab-like cards). Keep `state=\"default\"` if no persistent pressed state is needed; the component simply acts as a styled button.\n\nDefault padding is `8px`. Override via `style` to match the context:\n- `14px 16px` — emoji\n- `8px 16px` — label-only row\n- `10px 16px` — row with avatar\n- `24px` — large attendance card\n\n### Accessibility\n\n- Root element is `<button type=\"button\">`.\n- `aria-pressed` is `true` when `state` is `\"selected\"` or `\"done\"`, `false` when `\"default\"`.\n- For grouped single-select usage, wrap controls in a `<div role=\"group\" aria-label=\"...\">` so screen readers announce the group boundary.",
952
+ "props": [
953
+ {
954
+ "name": "state",
955
+ "description": "",
956
+ "options": [
957
+ "default",
958
+ "selected",
959
+ "done",
960
+ "done-selected"
961
+ ]
962
+ },
963
+ {
964
+ "name": "disabled",
965
+ "description": "",
966
+ "options": []
967
+ },
968
+ {
969
+ "name": "error",
970
+ "description": "",
971
+ "options": []
972
+ },
973
+ {
974
+ "name": "children",
975
+ "description": "",
976
+ "options": []
977
+ }
978
+ ],
979
+ "examples": [
980
+ {
981
+ "name": "AllStates",
982
+ "jsx": "// See \"AllStates\" story — uses interactive render wrapper"
983
+ },
984
+ {
985
+ "name": "SingleSelectGroup",
986
+ "jsx": "// See \"SingleSelectGroup\" story — uses interactive render wrapper"
987
+ },
988
+ {
989
+ "name": "LessonFeedbackExample",
990
+ "jsx": "// See \"LessonFeedbackExample\" story — uses interactive render wrapper"
991
+ }
992
+ ]
993
+ },
994
+ {
995
+ "name": "Skeleton",
996
+ "description": "Generic loading placeholder with a pulsing animation. Use it to build per-component skeleton screens.\n\n### Usage\n\n```tsx\nimport { Skeleton } from '@gostudent/shared-ui-library';\n\n<Skeleton width=\"60%\" height={20} />\n```",
997
+ "props": [
998
+ {
999
+ "name": "width",
1000
+ "description": "CSS width value or number (px).",
1001
+ "options": [
1002
+ "sm",
1003
+ "md",
1004
+ "lg",
1005
+ "full"
1006
+ ]
1007
+ }
1008
+ ],
1009
+ "examples": [
1010
+ {
1011
+ "name": "Default",
1012
+ "jsx": "<Skeleton width=\"100%\" height={20} />"
1013
+ },
1014
+ {
1015
+ "name": "Circle",
1016
+ "jsx": "<Skeleton width={40} height={40} radius=\"full\" />"
1017
+ }
1018
+ ]
1019
+ },
1020
+ {
1021
+ "name": "Stepper",
1022
+ "description": "A progress indicator for multi-step flows. Shows the user where they are in a sequence, how many steps remain, and — optionally — a keyboard hint to advance.\n\nThe component is display-only and full-width; its parent controls horizontal constraints. Progress fill animates smoothly as `currentStep` changes.\n\n### Usage\n\n```tsx\nimport { Stepper } from '@gostudent/shared-ui-library';\n\n<Stepper currentStep={2} totalSteps={4} />\n```\n\n### Accessibility\n\nThe progress bar track has `role=\"progressbar\"` with `aria-valuenow`, `aria-valuemin`, and `aria-valuemax` wired to the step props.\n\n### Notes\n\n- Always pass `totalSteps` as a dynamic prop — never hardcode the total.\n- Omit `enterHint` on mobile or touch-only flows where Enter is not meaningful.\n- On the final step the bar fills to exactly 100%.",
1023
+ "props": [
1024
+ {
1025
+ "name": "currentStep",
1026
+ "description": "Current step (1-based). Drives both the step label and the bar fill width.",
1027
+ "options": []
1028
+ },
1029
+ {
1030
+ "name": "totalSteps",
1031
+ "description": "Total number of steps. Pass from a prop or config — never hardcode.",
1032
+ "options": []
1033
+ },
1034
+ {
1035
+ "name": "stepLabel",
1036
+ "description": "Translated step label shown on the left (e.g. t(\\'stepper.label\\",
1037
+ "options": []
1038
+ },
1039
+ {
1040
+ "name": "enterHint",
1041
+ "description": "Translated hint text shown on the right (e.g. \"Press Enter to continue\"). Omit to hide the hint. The ↵ symbol is always appended automatically.",
1042
+ "options": []
1043
+ }
1044
+ ],
1045
+ "examples": [
1046
+ {
1047
+ "name": "Default",
1048
+ "jsx": "<Stepper currentStep={1} totalSteps={4} stepLabel=\"Step 1 of 4\" enterHint=\"Press Enter to continue\" />"
1049
+ },
1050
+ {
1051
+ "name": "Midpoint",
1052
+ "jsx": "<Stepper currentStep={2} totalSteps={4} stepLabel=\"Step 2 of 4\" enterHint=\"Press Enter to continue\" />"
1053
+ },
1054
+ {
1055
+ "name": "AlmostDone",
1056
+ "jsx": "<Stepper currentStep={3} totalSteps={4} stepLabel=\"Step 3 of 4\" enterHint=\"Press Enter to continue\" />"
1057
+ }
1058
+ ]
1059
+ },
1060
+ {
1061
+ "name": "Tabs",
1062
+ "description": "Compound tab component. Use `Tabs` as the root, `TabList` + `Tab` for the navigation bar,\nand `TabPanel` for the content areas. Tabs and panels are connected automatically via `value`.\n\n**Keyboard navigation:** Arrow Left/Right moves between tabs; Home/End jump to first/last.\n\n### Usage\n\n```tsx\nimport { Tabs, Tab, TabList, TabPanel } from '@gostudent/shared-ui-library';\n\n<Tabs defaultValue=\"lessons\">\n <TabList>\n <Tab value=\"lessons\">Lessons</Tab>\n <Tab value=\"students\">Students</Tab>\n </TabList>\n <TabPanel value=\"lessons\">Lessons content</TabPanel>\n <TabPanel value=\"students\">Students content</TabPanel>\n</Tabs>\n```\n\n**Standalone mode** (without `Tabs` wrapper — for custom state management):\n```tsx\n<TabList>\n <Tab isSelected={active === 0} onClick={() => setActive(0)}>Lessons</Tab>\n <Tab isSelected={active === 1} onClick={() => setActive(1)}>Students</Tab>\n</TabList>\n```",
1063
+ "props": [
1064
+ {
1065
+ "name": "defaultValue",
1066
+ "description": "Default selected tab value (uncontrolled).",
1067
+ "options": []
1068
+ },
1069
+ {
1070
+ "name": "value",
1071
+ "description": "Controlled selected tab value.",
1072
+ "options": []
1073
+ },
1074
+ {
1075
+ "name": "onChange",
1076
+ "description": "Called with the new value whenever the active tab changes.",
1077
+ "options": []
1078
+ },
1079
+ {
1080
+ "name": "className",
1081
+ "description": "Optional extra class name on the root element.",
1082
+ "options": []
1083
+ }
1084
+ ],
1085
+ "examples": [
1086
+ {
1087
+ "name": "Default",
1088
+ "jsx": "<Tabs defaultValue=\"lessons\" />"
1089
+ },
1090
+ {
1091
+ "name": "FullWidthMobile",
1092
+ "jsx": "<Tabs defaultValue=\"lessons\" />"
1093
+ },
1094
+ {
1095
+ "name": "FullWidth",
1096
+ "jsx": "<Tabs defaultValue=\"lessons\" />"
1097
+ }
1098
+ ]
1099
+ },
1100
+ {
1101
+ "name": "Tag",
1102
+ "description": "The **Tag** component is an agnostic label primitive. It comes in five semantic tones, two shapes (rounded / pill), three sizes, optional leading/trailing icons, and an optional action button.\n\n### Usage\n\n```tsx\nimport { Tag } from '@gostudent/shared-ui-library';\n\n<Tag tone=\"neutral\">Mathematics</Tag>\n<Tag tone=\"success\" bordered>Online</Tag>\n<Tag shape=\"pill\" tone=\"info\">Scheduled</Tag>\n<Tag trailingIcon={<CloseIcon size=\"sm\" />} onAction={() => remove()} actionLabel=\"Remove tag\">Math</Tag>\n```\n\n> **Domain variants** (student/tutor/parent roles, online/offline lesson status) live in the compositions layer:\n> `RoleTag` and `LessonStatusTag`.",
1103
+ "props": [
1104
+ {
1105
+ "name": "tone",
1106
+ "description": "Semantic tone — controls background, text and (when `bordered`) border colour.",
1107
+ "options": [
1108
+ "neutral",
1109
+ "success",
1110
+ "info",
1111
+ "warning",
1112
+ "danger"
1113
+ ]
1114
+ },
1115
+ {
1116
+ "name": "shape",
1117
+ "description": "`pill",
1118
+ "options": [
1119
+ "rounded",
1120
+ "pill"
1121
+ ]
1122
+ },
1123
+ {
1124
+ "name": "size",
1125
+ "description": "Size scale. Accepts a single size or a `[mobile, desktop]",
1126
+ "options": [
1127
+ "sm",
1128
+ "md"
1129
+ ]
1130
+ },
1131
+ {
1132
+ "name": "bordered",
1133
+ "description": "Adds a 1px border using the tone\\'s border colour.",
1134
+ "options": []
1135
+ },
1136
+ {
1137
+ "name": "children",
1138
+ "description": "Tag label text.",
1139
+ "options": []
1140
+ },
1141
+ {
1142
+ "name": "actionLabel",
1143
+ "description": "Required when `onAction",
1144
+ "options": []
1145
+ },
1146
+ {
1147
+ "name": "className",
1148
+ "description": "Optional extra CSS class for layout overrides.",
1149
+ "options": []
1150
+ }
1151
+ ],
1152
+ "examples": [
1153
+ {
1154
+ "name": "Default",
1155
+ "jsx": "<Tag tone=\"neutral\" size=\"md\" children=\"Mathematics\" />"
1156
+ },
1157
+ {
1158
+ "name": "Medium",
1159
+ "jsx": "<Tag size=\"md\" children=\"Mathematics\" />"
1160
+ },
1161
+ {
1162
+ "name": "Small",
1163
+ "jsx": "<Tag size=\"sm\" children=\"Mathematics\" />"
1164
+ }
1165
+ ]
1166
+ },
1167
+ {
1168
+ "name": "Tooltip",
1169
+ "description": "The **Tooltip** component wraps any trigger element and shows a floating label on hover or focus.\n\n### Usage\n\n```tsx\nimport { Tooltip } from '@gostudent/shared-ui-library';\n\n<Tooltip text=\"Lesson is in progress\" placement=\"top\">\n <svg .../>\n</Tooltip>\n```\n\nSupports an optional `title` (bold) above the body text, matching the Figma spec.",
1170
+ "props": [
1171
+ {
1172
+ "name": "placement",
1173
+ "description": "",
1174
+ "options": [
1175
+ "top",
1176
+ "bottom",
1177
+ "left",
1178
+ "right"
1179
+ ]
1180
+ }
1181
+ ],
1182
+ "examples": [
1183
+ {
1184
+ "name": "Default",
1185
+ "jsx": "<Tooltip text=\"I'm such a nice tooltip!\" placement=\"bottom\" />"
1186
+ },
1187
+ {
1188
+ "name": "WithTitle",
1189
+ "jsx": "<Tooltip title=\"Title\" text=\"I'm such a nice tooltip!\" placement=\"bottom\" />"
1190
+ }
1191
+ ]
1192
+ },
1193
+ {
1194
+ "name": "Typography",
1195
+ "description": "The **Typography** component renders styled text with built-in responsive behaviour.\nIt selects a sensible default HTML element for each variant and can be overridden with the `as` prop.\n\n### Usage\n\n```tsx\nimport { Typography } from '@gostudent/shared-ui-library';\n\n<Typography variant=\"heading-h1\">Hello world</Typography>\n<Typography variant=\"body-md\" as=\"span\">Inline text</Typography>\n```",
1196
+ "props": [
1197
+ {
1198
+ "name": "variant",
1199
+ "description": "Typography style variant.",
1200
+ "options": [
1201
+ "heading-h1",
1202
+ "heading-h2",
1203
+ "heading-h3",
1204
+ "heading-h4",
1205
+ "heading-h5",
1206
+ "heading-h6",
1207
+ "subtitle-md",
1208
+ "subtitle-sm",
1209
+ "body-md",
1210
+ "body-sm",
1211
+ "label",
1212
+ "tooltip"
1213
+ ]
1214
+ },
1215
+ {
1216
+ "name": "as",
1217
+ "description": "Override the rendered HTML element (e.g. `span",
1218
+ "options": []
1219
+ },
1220
+ {
1221
+ "name": "children",
1222
+ "description": "Text content.",
1223
+ "options": []
1224
+ }
1225
+ ],
1226
+ "examples": [
1227
+ {
1228
+ "name": "Default",
1229
+ "jsx": "<Typography variant=\"body-md\" children=\"The quick brown fox jumps over the lazy dog.\" />"
1230
+ }
1231
+ ]
1232
+ }
1233
+ ],
1234
+ "icons": {
1235
+ "names": [
1236
+ "ArrowLeftIcon",
1237
+ "ArrowRightIcon",
1238
+ "CancelFilledIcon",
1239
+ "ChevronDownIcon",
1240
+ "ChevronLeftIcon",
1241
+ "ChevronRightIcon",
1242
+ "ClockIcon",
1243
+ "CloseIcon",
1244
+ "DashboardIcon",
1245
+ "DeleteIcon",
1246
+ "EditSquareIcon",
1247
+ "ErrorFilledIcon",
1248
+ "EventNoteIcon",
1249
+ "GlobeIcon",
1250
+ "GroupIcon",
1251
+ "HistoryIcon",
1252
+ "HourglassIcon",
1253
+ "InfoFilledIcon",
1254
+ "InfoIcon",
1255
+ "KebabIcon",
1256
+ "LocationCenterIcon",
1257
+ "LogoutIcon",
1258
+ "MenuIcon",
1259
+ "OfflineIcon",
1260
+ "PlanetIcon",
1261
+ "PlusIcon",
1262
+ "StarsIcon",
1263
+ "TickFilledIcon",
1264
+ "TickIcon",
1265
+ "UploadIcon",
1266
+ "WarningFilledIcon"
1267
+ ],
1268
+ "sizes": [
1269
+ "sm",
1270
+ "md",
1271
+ "lg",
1272
+ "xl",
1273
+ "xxl",
1274
+ "3xl"
1275
+ ],
1276
+ "pixelMap": {
1277
+ "sm": 16,
1278
+ "md": 20,
1279
+ "lg": 24,
1280
+ "xl": 32,
1281
+ "xxl": 40
1282
+ }
1283
+ },
1284
+ "tokens": {
1285
+ "base": {
1286
+ "breakpoint": {
1287
+ "breakpointDesktopScreenWidth": "var(--breakpoint-desktop-screen-width)",
1288
+ "breakpointTabletScreenWidth": "var(--breakpoint-tablet-screen-width)",
1289
+ "breakpointMobileScreenWidth": "var(--breakpoint-mobile-screen-width)",
1290
+ "breakpointLayoutContentMaxWidth": "var(--breakpoint-layout-content-max-width)"
1291
+ },
1292
+ "error": {
1293
+ "error50": "var(--error-50)",
1294
+ "error500": "var(--error-500)",
1295
+ "error700": "var(--error-700)"
1296
+ },
1297
+ "success": {
1298
+ "success50": "var(--success-50)",
1299
+ "success500": "var(--success-500)",
1300
+ "success700": "var(--success-700)"
1301
+ },
1302
+ "warning": {
1303
+ "warning50": "var(--warning-50)",
1304
+ "warning500": "var(--warning-500)",
1305
+ "warning700": "var(--warning-700)"
1306
+ },
1307
+ "color": {
1308
+ "colorBlack": "var(--color-black)",
1309
+ "colorWhite": "var(--color-white)",
1310
+ "colorTransparent": "var(--color-transparent)"
1311
+ },
1312
+ "steel": {
1313
+ "steel50": "var(--steel-50)",
1314
+ "steel100": "var(--steel-100)",
1315
+ "steel200": "var(--steel-200)",
1316
+ "steel300": "var(--steel-300)",
1317
+ "steel400": "var(--steel-400)",
1318
+ "steel500": "var(--steel-500)",
1319
+ "steel600": "var(--steel-600)",
1320
+ "steel700": "var(--steel-700)",
1321
+ "steel800": "var(--steel-800)",
1322
+ "steel900": "var(--steel-900)"
1323
+ },
1324
+ "grey": {
1325
+ "grey50": "var(--grey-50)",
1326
+ "grey100": "var(--grey-100)",
1327
+ "grey200": "var(--grey-200)",
1328
+ "grey300": "var(--grey-300)",
1329
+ "grey400": "var(--grey-400)",
1330
+ "grey500": "var(--grey-500)",
1331
+ "grey600": "var(--grey-600)",
1332
+ "grey700": "var(--grey-700)",
1333
+ "grey800": "var(--grey-800)",
1334
+ "grey900": "var(--grey-900)"
1335
+ },
1336
+ "in": {
1337
+ "inProgressLight": "var(--in-progress-light)",
1338
+ "inProgressDark": "var(--in-progress-dark)"
1339
+ },
1340
+ "spacing": {
1341
+ "spacingXxs": "var(--spacing-xxs)",
1342
+ "spacing3xl": "var(--spacing-3xl)",
1343
+ "spacing2xl": "var(--spacing-2xl)",
1344
+ "spacingXl": "var(--spacing-xl)",
1345
+ "spacingLg": "var(--spacing-lg)",
1346
+ "spacingXs": "var(--spacing-xs)",
1347
+ "spacingMd": "var(--spacing-md)",
1348
+ "spacingSm": "var(--spacing-sm)",
1349
+ "spacing3xs": "var(--spacing-3xs)"
1350
+ },
1351
+ "radius": {
1352
+ "radiusXs": "var(--radius-xs)",
1353
+ "radiusSm": "var(--radius-sm)",
1354
+ "radiusMd": "var(--radius-md)",
1355
+ "radiusLg": "var(--radius-lg)",
1356
+ "radiusXl": "var(--radius-xl)",
1357
+ "radiusFull": "var(--radius-full)"
1358
+ },
1359
+ "border": {
1360
+ "borderSm": "var(--border-sm)",
1361
+ "borderMd": "var(--border-md)",
1362
+ "borderLg": "var(--border-lg)",
1363
+ "borderXs": "var(--border-xs)"
1364
+ },
1365
+ "font": {
1366
+ "fontFamilyWorksans": "var(--font-family-worksans)",
1367
+ "fontWeightRegular": "var(--font-weight-regular)",
1368
+ "fontWeightMedium": "var(--font-weight-medium)",
1369
+ "fontWeightSemiBold": "var(--font-weight-semi-bold)",
1370
+ "fontWeightBold": "var(--font-weight-bold)",
1371
+ "fontWeightExtraBold": "var(--font-weight-extra-bold)",
1372
+ "fontSizeXxs": "var(--font-size-xxs)",
1373
+ "fontSizeXs": "var(--font-size-xs)",
1374
+ "fontSizeMd": "var(--font-size-md)",
1375
+ "fontSizeLg": "var(--font-size-lg)",
1376
+ "fontSizeXl": "var(--font-size-xl)",
1377
+ "fontSize2xl": "var(--font-size-2xl)",
1378
+ "fontSize3xl": "var(--font-size-3xl)",
1379
+ "fontSize4xl": "var(--font-size-4xl)",
1380
+ "fontSizeSm": "var(--font-size-sm)",
1381
+ "fontLineHeightXxs": "var(--font-line-height-xxs)",
1382
+ "fontLineHeightXs": "var(--font-line-height-xs)",
1383
+ "fontLineHeightMd": "var(--font-line-height-md)",
1384
+ "fontLineHeightLg": "var(--font-line-height-lg)",
1385
+ "fontLineHeightXl": "var(--font-line-height-xl)",
1386
+ "fontLineHeight2xl": "var(--font-line-height-2xl)",
1387
+ "fontLineHeight3xl": "var(--font-line-height-3xl)",
1388
+ "fontLineHeight4xl": "var(--font-line-height-4xl)",
1389
+ "fontLineHeightSm": "var(--font-line-height-sm)"
1390
+ },
1391
+ "avatar": {
1392
+ "avatarSm": "var(--avatar-sm)",
1393
+ "avatarMd": "var(--avatar-md)",
1394
+ "avatarLg": "var(--avatar-lg)",
1395
+ "avatarXs": "var(--avatar-xs)"
1396
+ },
1397
+ "icon": {
1398
+ "iconButtonSm": "var(--icon-button-sm)",
1399
+ "iconButtonMd": "var(--icon-button-md)",
1400
+ "iconButtonLg": "var(--icon-button-lg)",
1401
+ "iconSm": "var(--icon-sm)",
1402
+ "iconMd": "var(--icon-md)",
1403
+ "iconLg": "var(--icon-lg)",
1404
+ "iconXl": "var(--icon-xl)",
1405
+ "iconXxl": "var(--icon-xxl)"
1406
+ }
1407
+ },
1408
+ "gs": {
1409
+ "blue": {
1410
+ "blue50": "var(--blue-50)",
1411
+ "blue100": "var(--blue-100)",
1412
+ "blue200": "var(--blue-200)",
1413
+ "blue300": "var(--blue-300)",
1414
+ "blue400": "var(--blue-400)",
1415
+ "blue500": "var(--blue-500)",
1416
+ "blue600": "var(--blue-600)",
1417
+ "blue700": "var(--blue-700)",
1418
+ "blue800": "var(--blue-800)",
1419
+ "blue900": "var(--blue-900)"
1420
+ },
1421
+ "purple": {
1422
+ "purple50": "var(--purple-50)",
1423
+ "purple100": "var(--purple-100)",
1424
+ "purple200": "var(--purple-200)",
1425
+ "purple300": "var(--purple-300)",
1426
+ "purple400": "var(--purple-400)",
1427
+ "purple500": "var(--purple-500)",
1428
+ "purple600": "var(--purple-600)",
1429
+ "purple700": "var(--purple-700)",
1430
+ "purple800": "var(--purple-800)",
1431
+ "purple900": "var(--purple-900)"
1432
+ },
1433
+ "orange": {
1434
+ "orange50": "var(--orange-50)",
1435
+ "orange100": "var(--orange-100)",
1436
+ "orange200": "var(--orange-200)",
1437
+ "orange300": "var(--orange-300)",
1438
+ "orange400": "var(--orange-400)",
1439
+ "orange500": "var(--orange-500)",
1440
+ "orange600": "var(--orange-600)",
1441
+ "orange700": "var(--orange-700)",
1442
+ "orange800": "var(--orange-800)",
1443
+ "orange900": "var(--orange-900)"
1444
+ },
1445
+ "green": {
1446
+ "green50": "var(--green-50)",
1447
+ "green100": "var(--green-100)",
1448
+ "green200": "var(--green-200)",
1449
+ "green300": "var(--green-300)",
1450
+ "green400": "var(--green-400)",
1451
+ "green500": "var(--green-500)",
1452
+ "green600": "var(--green-600)",
1453
+ "green700": "var(--green-700)",
1454
+ "green800": "var(--green-800)",
1455
+ "green900": "var(--green-900)"
1456
+ },
1457
+ "yellow": {
1458
+ "yellow50": "var(--yellow-50)",
1459
+ "yellow100": "var(--yellow-100)",
1460
+ "yellow200": "var(--yellow-200)",
1461
+ "yellow300": "var(--yellow-300)",
1462
+ "yellow400": "var(--yellow-400)",
1463
+ "yellow500": "var(--yellow-500)",
1464
+ "yellow600": "var(--yellow-600)",
1465
+ "yellow700": "var(--yellow-700)",
1466
+ "yellow800": "var(--yellow-800)",
1467
+ "yellow900": "var(--yellow-900)"
1468
+ },
1469
+ "pink": {
1470
+ "pink50": "var(--pink-50)",
1471
+ "pink100": "var(--pink-100)",
1472
+ "pink200": "var(--pink-200)",
1473
+ "pink300": "var(--pink-300)",
1474
+ "pink400": "var(--pink-400)",
1475
+ "pink500": "var(--pink-500)",
1476
+ "pink600": "var(--pink-600)",
1477
+ "pink700": "var(--pink-700)",
1478
+ "pink800": "var(--pink-800)",
1479
+ "pink900": "var(--pink-900)"
1480
+ }
1481
+ },
1482
+ "sk": {
1483
+ "blue": {
1484
+ "blue50": "var(--blue-50)",
1485
+ "blue100": "var(--blue-100)",
1486
+ "blue200": "var(--blue-200)",
1487
+ "blue300": "var(--blue-300)",
1488
+ "blue400": "var(--blue-400)",
1489
+ "blue500": "var(--blue-500)",
1490
+ "blue600": "var(--blue-600)",
1491
+ "blue700": "var(--blue-700)",
1492
+ "blue800": "var(--blue-800)",
1493
+ "blue900": "var(--blue-900)"
1494
+ },
1495
+ "red": {
1496
+ "red50": "var(--red-50)",
1497
+ "red100": "var(--red-100)",
1498
+ "red200": "var(--red-200)",
1499
+ "red300": "var(--red-300)",
1500
+ "red400": "var(--red-400)",
1501
+ "red500": "var(--red-500)",
1502
+ "red600": "var(--red-600)",
1503
+ "red700": "var(--red-700)",
1504
+ "red800": "var(--red-800)",
1505
+ "red900": "var(--red-900)"
1506
+ },
1507
+ "orange": {
1508
+ "orange50": "var(--orange-50)",
1509
+ "orange100": "var(--orange-100)",
1510
+ "orange200": "var(--orange-200)",
1511
+ "orange300": "var(--orange-300)",
1512
+ "orange400": "var(--orange-400)",
1513
+ "orange500": "var(--orange-500)",
1514
+ "orange600": "var(--orange-600)",
1515
+ "orange700": "var(--orange-700)",
1516
+ "orange800": "var(--orange-800)",
1517
+ "orange900": "var(--orange-900)"
1518
+ },
1519
+ "green": {
1520
+ "green50": "var(--green-50)",
1521
+ "green100": "var(--green-100)",
1522
+ "green200": "var(--green-200)",
1523
+ "green300": "var(--green-300)",
1524
+ "green400": "var(--green-400)",
1525
+ "green500": "var(--green-500)",
1526
+ "green600": "var(--green-600)",
1527
+ "green700": "var(--green-700)",
1528
+ "green800": "var(--green-800)",
1529
+ "green900": "var(--green-900)"
1530
+ },
1531
+ "yellow": {
1532
+ "yellow50": "var(--yellow-50)",
1533
+ "yellow100": "var(--yellow-100)",
1534
+ "yellow200": "var(--yellow-200)",
1535
+ "yellow300": "var(--yellow-300)",
1536
+ "yellow400": "var(--yellow-400)",
1537
+ "yellow500": "var(--yellow-500)",
1538
+ "yellow600": "var(--yellow-600)",
1539
+ "yellow700": "var(--yellow-700)",
1540
+ "yellow800": "var(--yellow-800)",
1541
+ "yellow900": "var(--yellow-900)"
1542
+ },
1543
+ "pink": {
1544
+ "pinkPurple50": "var(--pink-purple-50)",
1545
+ "pinkPurple100": "var(--pink-purple-100)",
1546
+ "pinkPurple200": "var(--pink-purple-200)",
1547
+ "pinkPurple300": "var(--pink-purple-300)",
1548
+ "pinkPurple400": "var(--pink-purple-400)",
1549
+ "pinkPurple500": "var(--pink-purple-500)",
1550
+ "pinkPurple600": "var(--pink-purple-600)",
1551
+ "pinkPurple700": "var(--pink-purple-700)",
1552
+ "pinkPurple800": "var(--pink-purple-800)",
1553
+ "pinkPurple900": "var(--pink-purple-900)"
1554
+ },
1555
+ "light": {
1556
+ "lightBlue50": "var(--light-blue-50)",
1557
+ "lightBlue100": "var(--light-blue-100)",
1558
+ "lightBlue200": "var(--light-blue-200)",
1559
+ "lightBlue300": "var(--light-blue-300)",
1560
+ "lightBlue400": "var(--light-blue-400)",
1561
+ "lightBlue500": "var(--light-blue-500)",
1562
+ "lightBlue600": "var(--light-blue-600)",
1563
+ "lightBlue700": "var(--light-blue-700)",
1564
+ "lightBlue800": "var(--light-blue-800)",
1565
+ "lightBlue900": "var(--light-blue-900)"
1566
+ }
1567
+ }
1568
+ }
1569
+ }