@arbor-education/design-system.components 0.13.0 → 0.14.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.
Files changed (232) hide show
  1. package/.agent-memory/blanche-designspert/MEMORY.md +189 -0
  2. package/.agent-memory/dorothy-fact-checker/MEMORY.md +228 -0
  3. package/.agent-memory/dorothy-fact-checker/numberinput_component.md +53 -0
  4. package/.agent-memory/dorothy-fact-checker/progress_component.md +36 -0
  5. package/.agent-memory/rose-storybookspert/MEMORY.md +105 -0
  6. package/.agent-memory/sophia-componentspert/MEMORY.md +34 -0
  7. package/{.claude/agent-memory → .agent-memory}/sophia-componentspert/components.md +170 -17
  8. package/{.claude → .gather}/agents/blanche-designspert.md +7 -2
  9. package/{.claude → .gather}/agents/dorothy-fact-checker.md +7 -2
  10. package/{.claude → .gather}/agents/rose-storybookspert.md +80 -11
  11. package/{.claude → .gather}/agents/sophia-componentspert.md +9 -4
  12. package/.gather/gather.yaml +9 -0
  13. package/{CLAUDE.md → .gather/instructions/project-overview.md} +42 -9
  14. package/{.claude → .gather}/skills/analyze-design/README.md +5 -0
  15. package/{.claude → .gather}/skills/analyze-design/SKILL.md +1 -1
  16. package/.gather/skills/analyze-design/meta.md +4 -0
  17. package/{.claude → .gather}/skills/create-page/README.md +5 -0
  18. package/{.claude → .gather}/skills/create-page/design-analysis-template.md +5 -0
  19. package/.gather/skills/create-page/meta.md +4 -0
  20. package/{.claude → .gather}/skills/create-page/page-template.scss +5 -0
  21. package/{.claude → .gather}/skills/create-page/page-template.tsx +5 -0
  22. package/{.claude → .gather}/skills/map-legacy/README.md +5 -0
  23. package/.gather/skills/map-legacy/meta.md +4 -0
  24. package/{.claude → .gather}/skills/migrate-page/README.md +5 -0
  25. package/.gather/skills/migrate-page/meta.md +4 -0
  26. package/.gather/skills/write-stories/README.md +157 -0
  27. package/.gather/skills/write-stories/SKILL.md +841 -0
  28. package/.gather/skills/write-stories/meta.md +4 -0
  29. package/.ralph/storybook-upgrade/knowledge.md +308 -0
  30. package/.ralph/storybook-upgrade/prd.json +777 -0
  31. package/.ralph/storybook-upgrade/progress.md +342 -0
  32. package/.storybook/DocsTemplate.tsx +122 -0
  33. package/.storybook/preview.ts +40 -0
  34. package/.stylelintignore +2 -0
  35. package/CHANGELOG.md +14 -0
  36. package/{.claude/component-library.md → component-library.md} +27 -10
  37. package/dist/components/articleCard/ArticleCard.d.ts +30 -0
  38. package/dist/components/articleCard/ArticleCard.d.ts.map +1 -0
  39. package/dist/components/articleCard/ArticleCard.js +24 -0
  40. package/dist/components/articleCard/ArticleCard.js.map +1 -0
  41. package/dist/components/articleCard/ArticleCard.stories.d.ts +18 -0
  42. package/dist/components/articleCard/ArticleCard.stories.d.ts.map +1 -0
  43. package/dist/components/articleCard/ArticleCard.stories.js +112 -0
  44. package/dist/components/articleCard/ArticleCard.stories.js.map +1 -0
  45. package/dist/components/articleCard/ArticleCard.test.d.ts +2 -0
  46. package/dist/components/articleCard/ArticleCard.test.d.ts.map +1 -0
  47. package/dist/components/articleCard/ArticleCard.test.js +49 -0
  48. package/dist/components/articleCard/ArticleCard.test.js.map +1 -0
  49. package/dist/components/badge/Badge.stories.d.ts +85 -6
  50. package/dist/components/badge/Badge.stories.d.ts.map +1 -1
  51. package/dist/components/badge/Badge.stories.js +626 -27
  52. package/dist/components/badge/Badge.stories.js.map +1 -1
  53. package/dist/components/banner/Banner.stories.d.ts +129 -63
  54. package/dist/components/banner/Banner.stories.d.ts.map +1 -1
  55. package/dist/components/banner/Banner.stories.js +855 -39
  56. package/dist/components/banner/Banner.stories.js.map +1 -1
  57. package/dist/components/button/Button.stories.d.ts +148 -8
  58. package/dist/components/button/Button.stories.d.ts.map +1 -1
  59. package/dist/components/button/Button.stories.js +1089 -80
  60. package/dist/components/button/Button.stories.js.map +1 -1
  61. package/dist/components/card/Card.d.ts +41 -12
  62. package/dist/components/card/Card.d.ts.map +1 -1
  63. package/dist/components/card/Card.js +46 -17
  64. package/dist/components/card/Card.js.map +1 -1
  65. package/dist/components/card/Card.stories.d.ts +9 -84
  66. package/dist/components/card/Card.stories.d.ts.map +1 -1
  67. package/dist/components/card/Card.stories.js +15 -73
  68. package/dist/components/card/Card.stories.js.map +1 -1
  69. package/dist/components/card/Card.test.js +50 -152
  70. package/dist/components/card/Card.test.js.map +1 -1
  71. package/dist/components/dot/Dot.stories.d.ts +46 -11
  72. package/dist/components/dot/Dot.stories.d.ts.map +1 -1
  73. package/dist/components/dot/Dot.stories.js +504 -15
  74. package/dist/components/dot/Dot.stories.js.map +1 -1
  75. package/dist/components/dropdown/Dropdown.stories.d.ts +89 -14
  76. package/dist/components/dropdown/Dropdown.stories.d.ts.map +1 -1
  77. package/dist/components/dropdown/Dropdown.stories.js +769 -17
  78. package/dist/components/dropdown/Dropdown.stories.js.map +1 -1
  79. package/dist/components/formField/FormField.stories.d.ts +95 -35
  80. package/dist/components/formField/FormField.stories.d.ts.map +1 -1
  81. package/dist/components/formField/FormField.stories.js +1174 -69
  82. package/dist/components/formField/FormField.stories.js.map +1 -1
  83. package/dist/components/formField/inputs/checkbox/CheckboxInput.stories.d.ts +96 -9
  84. package/dist/components/formField/inputs/checkbox/CheckboxInput.stories.d.ts.map +1 -1
  85. package/dist/components/formField/inputs/checkbox/CheckboxInput.stories.js +717 -10
  86. package/dist/components/formField/inputs/checkbox/CheckboxInput.stories.js.map +1 -1
  87. package/dist/components/formField/inputs/number/NumberInput.stories.d.ts +149 -11
  88. package/dist/components/formField/inputs/number/NumberInput.stories.d.ts.map +1 -1
  89. package/dist/components/formField/inputs/number/NumberInput.stories.js +624 -10
  90. package/dist/components/formField/inputs/number/NumberInput.stories.js.map +1 -1
  91. package/dist/components/formField/inputs/radio/RadioButtonInput.stories.d.ts +74 -1
  92. package/dist/components/formField/inputs/radio/RadioButtonInput.stories.d.ts.map +1 -1
  93. package/dist/components/formField/inputs/radio/RadioButtonInput.stories.js +673 -44
  94. package/dist/components/formField/inputs/radio/RadioButtonInput.stories.js.map +1 -1
  95. package/dist/components/formField/inputs/text/TextInput.stories.d.ts +119 -1
  96. package/dist/components/formField/inputs/text/TextInput.stories.d.ts.map +1 -1
  97. package/dist/components/formField/inputs/text/TextInput.stories.js +549 -10
  98. package/dist/components/formField/inputs/text/TextInput.stories.js.map +1 -1
  99. package/dist/components/formField/inputs/textArea/TextArea.stories.d.ts +129 -4
  100. package/dist/components/formField/inputs/textArea/TextArea.stories.d.ts.map +1 -1
  101. package/dist/components/formField/inputs/textArea/TextArea.stories.js +577 -3
  102. package/dist/components/formField/inputs/textArea/TextArea.stories.js.map +1 -1
  103. package/dist/components/formField/inputs/time/TimeInput.d.ts +1 -1
  104. package/dist/components/formField/inputs/time/TimeInput.stories.d.ts +1 -1
  105. package/dist/components/heading/Heading.stories.d.ts +449 -50
  106. package/dist/components/heading/Heading.stories.d.ts.map +1 -1
  107. package/dist/components/heading/Heading.stories.js +536 -60
  108. package/dist/components/heading/Heading.stories.js.map +1 -1
  109. package/dist/components/icoText/IcoText.d.ts +37 -0
  110. package/dist/components/icoText/IcoText.d.ts.map +1 -0
  111. package/dist/components/icoText/IcoText.js +29 -0
  112. package/dist/components/icoText/IcoText.js.map +1 -0
  113. package/dist/components/icoText/IcoText.stories.d.ts +34 -0
  114. package/dist/components/icoText/IcoText.stories.d.ts.map +1 -0
  115. package/dist/components/icoText/IcoText.stories.js +24 -0
  116. package/dist/components/icoText/IcoText.stories.js.map +1 -0
  117. package/dist/components/icoText/IcoText.test.d.ts +2 -0
  118. package/dist/components/icoText/IcoText.test.d.ts.map +1 -0
  119. package/dist/components/icoText/IcoText.test.js +27 -0
  120. package/dist/components/icoText/IcoText.test.js.map +1 -0
  121. package/dist/components/icon/Icon.stories.d.ts +81 -10
  122. package/dist/components/icon/Icon.stories.d.ts.map +1 -1
  123. package/dist/components/icon/Icon.stories.js +979 -8
  124. package/dist/components/icon/Icon.stories.js.map +1 -1
  125. package/dist/components/kpiCard/KPICard.d.ts +13 -0
  126. package/dist/components/kpiCard/KPICard.d.ts.map +1 -0
  127. package/dist/components/kpiCard/KPICard.js +8 -0
  128. package/dist/components/kpiCard/KPICard.js.map +1 -0
  129. package/dist/components/kpiCard/KPICard.stories.d.ts +9 -0
  130. package/dist/components/kpiCard/KPICard.stories.d.ts.map +1 -0
  131. package/dist/components/kpiCard/KPICard.stories.js +18 -0
  132. package/dist/components/kpiCard/KPICard.stories.js.map +1 -0
  133. package/dist/components/kpiCard/KPICard.test.d.ts +2 -0
  134. package/dist/components/kpiCard/KPICard.test.d.ts.map +1 -0
  135. package/dist/components/kpiCard/KPICard.test.js +37 -0
  136. package/dist/components/kpiCard/KPICard.test.js.map +1 -0
  137. package/dist/components/kvpList/KVPList.d.ts +34 -0
  138. package/dist/components/kvpList/KVPList.d.ts.map +1 -0
  139. package/dist/components/kvpList/KVPList.js +20 -0
  140. package/dist/components/kvpList/KVPList.js.map +1 -0
  141. package/dist/components/kvpList/KVPList.stories.d.ts +27 -0
  142. package/dist/components/kvpList/KVPList.stories.d.ts.map +1 -0
  143. package/dist/components/kvpList/KVPList.stories.js +18 -0
  144. package/dist/components/kvpList/KVPList.stories.js.map +1 -0
  145. package/dist/components/kvpList/KVPList.test.d.ts +2 -0
  146. package/dist/components/kvpList/KVPList.test.d.ts.map +1 -0
  147. package/dist/components/kvpList/KVPList.test.js +29 -0
  148. package/dist/components/kvpList/KVPList.test.js.map +1 -0
  149. package/dist/components/pill/Pill.stories.d.ts +71 -19
  150. package/dist/components/pill/Pill.stories.d.ts.map +1 -1
  151. package/dist/components/pill/Pill.stories.js +573 -14
  152. package/dist/components/pill/Pill.stories.js.map +1 -1
  153. package/dist/components/progress/Progress.stories.d.ts +75 -298
  154. package/dist/components/progress/Progress.stories.d.ts.map +1 -1
  155. package/dist/components/progress/Progress.stories.js +449 -52
  156. package/dist/components/progress/Progress.stories.js.map +1 -1
  157. package/dist/components/separator/Separator.stories.d.ts +58 -5
  158. package/dist/components/separator/Separator.stories.d.ts.map +1 -1
  159. package/dist/components/separator/Separator.stories.js +443 -4
  160. package/dist/components/separator/Separator.stories.js.map +1 -1
  161. package/dist/components/singleUser/SingleUser.d.ts +1 -1
  162. package/dist/components/tabs/TabsItem.stories.d.ts +2 -2
  163. package/dist/components/tag/Tag.stories.d.ts +116 -5
  164. package/dist/components/tag/Tag.stories.d.ts.map +1 -1
  165. package/dist/components/tag/Tag.stories.js +581 -28
  166. package/dist/components/tag/Tag.stories.js.map +1 -1
  167. package/dist/index.css +194 -23
  168. package/dist/index.css.map +1 -1
  169. package/dist/index.d.ts +13 -4
  170. package/dist/index.d.ts.map +1 -1
  171. package/dist/index.js +12 -3
  172. package/dist/index.js.map +1 -1
  173. package/eslint.config.mts +5 -1
  174. package/package.json +3 -3
  175. package/src/components/articleCard/ArticleCard.stories.tsx +132 -0
  176. package/src/components/articleCard/ArticleCard.test.tsx +121 -0
  177. package/src/components/articleCard/ArticleCard.tsx +100 -0
  178. package/src/components/articleCard/articleCard.scss +39 -0
  179. package/src/components/badge/Badge.stories.tsx +869 -42
  180. package/src/components/banner/Banner.stories.tsx +1081 -63
  181. package/src/components/button/Button.stories.tsx +1394 -99
  182. package/src/components/card/Card.stories.tsx +35 -79
  183. package/src/components/card/Card.test.tsx +72 -190
  184. package/src/components/card/Card.tsx +117 -58
  185. package/src/components/card/card.scss +18 -31
  186. package/src/components/dot/Dot.stories.tsx +723 -32
  187. package/src/components/dropdown/Dropdown.stories.tsx +1174 -35
  188. package/src/components/formField/FormField.stories.tsx +1522 -105
  189. package/src/components/formField/inputs/checkbox/CheckboxInput.stories.tsx +1020 -15
  190. package/src/components/formField/inputs/number/NumberInput.stories.tsx +908 -15
  191. package/src/components/formField/inputs/radio/RadioButtonInput.stories.tsx +932 -51
  192. package/src/components/formField/inputs/text/TextInput.stories.tsx +773 -13
  193. package/src/components/formField/inputs/textArea/TextArea.stories.tsx +756 -8
  194. package/src/components/heading/Heading.stories.tsx +752 -120
  195. package/src/components/icoText/IcoText.stories.tsx +47 -0
  196. package/src/components/icoText/IcoText.test.tsx +41 -0
  197. package/src/components/icoText/IcoText.tsx +93 -0
  198. package/src/components/icoText/icoText.scss +34 -0
  199. package/src/components/icon/Icon.stories.tsx +1446 -12
  200. package/src/components/kpiCard/KPICard.stories.tsx +47 -0
  201. package/src/components/kpiCard/KPICard.test.tsx +60 -0
  202. package/src/components/kpiCard/KPICard.tsx +45 -0
  203. package/src/components/kpiCard/kpiCard.scss +35 -0
  204. package/src/components/kvpList/KVPList.stories.tsx +51 -0
  205. package/src/components/kvpList/KVPList.test.tsx +66 -0
  206. package/src/components/kvpList/KVPList.tsx +109 -0
  207. package/src/components/kvpList/kvpList.scss +64 -0
  208. package/src/components/pill/Pill.stories.tsx +867 -21
  209. package/src/components/progress/Progress.stories.tsx +625 -58
  210. package/src/components/separator/Separator.stories.tsx +730 -8
  211. package/src/components/separator/separator.scss +12 -3
  212. package/src/components/tag/Tag.stories.tsx +755 -53
  213. package/src/index.scss +4 -0
  214. package/src/index.ts +13 -4
  215. package/src/tokens.scss +6 -0
  216. package/tokens/json/Arbor.json +30 -0
  217. package/.claude/agent-memory/blanche-designspert/MEMORY.md +0 -64
  218. package/.claude/agent-memory/dorothy-fact-checker/MEMORY.md +0 -129
  219. package/.claude/agent-memory/rose-storybookspert/MEMORY.md +0 -29
  220. package/.claude/agent-memory/sophia-componentspert/MEMORY.md +0 -14
  221. package/.claude/design-assessment-daily-attendance-2026-04-10.md +0 -566
  222. package/.claude/figma-assessment-7154-58899.md +0 -404
  223. package/.claude/figma-assessment-UKQfcxnT4rlHHNuiumt4o1-11086-97537.md +0 -392
  224. package/.claude/figma-assessment-UKQfcxnT4rlHHNuiumt4o1-551-41974.md +0 -474
  225. package/.claude/figma-assessment-UKQfcxnT4rlHHNuiumt4o1-551-43094.md +0 -462
  226. package/.claude/figma-assessment-fcFK4CGzkz2fVyY3koX8ZE-7154-59061.md +0 -440
  227. package/.claude/migration-report-custom-report-writer-2026-02-19.md +0 -591
  228. /package/{.claude/agent-memory → .agent-memory}/blanche-designspert/token-review-patterns.md +0 -0
  229. /package/{.claude/agent-memory → .agent-memory}/rose-storybookspert/patterns.md +0 -0
  230. /package/{.claude → .gather}/skills/create-page/SKILL.md +0 -0
  231. /package/{.claude → .gather}/skills/map-legacy/SKILL.md +0 -0
  232. /package/{.claude → .gather}/skills/migrate-page/SKILL.md +0 -0
@@ -1,392 +0,0 @@
1
- # Figma Design Feasibility Assessment
2
- ## Design: Node 11086:97537 — Take Register (Attendance) Page
3
- **Written by:** Sophia Petrillo, Componentspert, age none-of-your-business
4
- **Fact-checked by:** Dorothy Zbornak, QA Expert
5
- **Date**: 2026-04-10
6
-
7
- ---
8
-
9
- ## What Is This Thing, Anyway?
10
-
11
- Picture it: Sicily, 1928. My mother needed to take the register at the village school. She had twelve bambini, one piece of chalk, and absolutely no component library. And you know what? She built the whole thing from scratch with her bare hands.
12
-
13
- TODAY, we have a design system. So let's not build it all from scratch — but we will need to build *some* things.
14
-
15
- The "Take Register" page is an attendance marking interface for a school register ("Reception: Form NMC · Tue, 14 Feb 2023, 13:39 (P5)"). It's a full-width 1366px layout with a COLLAPSED left sidebar (only 58px wide — icon-only, no labels), and a main content area showing 12 student rows, each containing a photo, attendance mark toggle buttons, and a mini attendance history panel. The primary interaction is teachers clicking "Present / Absent / Late / Skip" for each student.
16
-
17
- Eighteen distinct UI elements. Some are gift-wrapped. Some require building. Let's go.
18
-
19
- ---
20
-
21
- ## Design Overview
22
-
23
- **Register | Desktop 1366px** — Attendance register marking interface for a school class.
24
-
25
- ### UI Elements Identified
26
-
27
- - Top navigation bar (logo, nav links, global search, Ask Arbor AI button, user avatar)
28
- - Left side navigation — **COLLAPSED/ICON-ONLY variant** (58px wide — different from 232px expanded)
29
- - Breadcrumbs (Lesson / Lesson Dashboard / Attendance)
30
- - Back button ("< Back")
31
- - Page heading H1 "Take Register"
32
- - Sub-info text row ("Reception: Form NMC · Tue, 14 Feb 2023, 13:39 (P5)")
33
- - Segmented icon button group — Profile View / List View toggle (270px × 52px pill container)
34
- - "Mark All Blank As..." primary dropdown button
35
- - Info/filter row with form label and bulk action
36
- - Register tiles (12 student rows, each 1268px × 119px) containing:
37
- - Student photo avatar (~50px circle)
38
- - Student name text
39
- - Attendance mark toggle buttons (✕ Absent · ◎ Late · ✓ Present · + Skip)
40
- - Today's period summary marks
41
- - Attendance history mini-grid (Reg / P1–P5 columns with colored mark indicators)
42
- - Optional comment/note text (e.g. "Family Holiday (not agreed)", "Late 178 mins")
43
- - Separator/dividers between register tile rows
44
-
45
- ---
46
-
47
- ## Section 1: What We Already Have — Use These As-Is, Don't Reinvent Them
48
-
49
- ### Back Button (`src/components/button/Button.tsx`)
50
-
51
- Simple. Dorothy confirmed `'arrow-left'` exists (line 102 of allowedIcons).
52
-
53
- ```tsx
54
- <Button variant="secondary" size="S" iconLeftName="arrow-left">
55
- Back
56
- </Button>
57
- ```
58
-
59
- ---
60
-
61
- ### Page Heading (`src/components/heading/Heading.tsx`)
62
-
63
- One line. No drama.
64
-
65
- ```tsx
66
- <Heading level={1}>Take Register</Heading>
67
- ```
68
-
69
- ---
70
-
71
- ### Sub-info Text Row
72
-
73
- Plain HTML with DS typography tokens. No component needed. If "Form NMC" appears in a pill/badge treatment, `<Tag color="neutral" text="Form NMC" />` works.
74
-
75
- ```tsx
76
- <p className="ds-register__sub-info">
77
- Reception: Form NMC · Tue, 14 Feb 2023, 13:39 (P5)
78
- </p>
79
- ```
80
-
81
- ---
82
-
83
- ### "Mark All Blank As..." Dropdown (`src/components/dropdown/Dropdown.tsx`)
84
-
85
- `Dropdown` compound + `Button variant="primary"`. Exactly what the component was built for.
86
-
87
- ```tsx
88
- <Dropdown>
89
- <Dropdown.Trigger asChild>
90
- <Button variant="primary" iconRightName="chevron-down">
91
- Mark All Blank As...
92
- </Button>
93
- </Dropdown.Trigger>
94
- <Dropdown.Content>
95
- <Dropdown.Item onSelect={() => handleMarkAll('present')}>Present</Dropdown.Item>
96
- <Dropdown.Item onSelect={() => handleMarkAll('absent')}>Absent</Dropdown.Item>
97
- <Dropdown.Item onSelect={() => handleMarkAll('late')}>Late</Dropdown.Item>
98
- <Dropdown.Item onSelect={() => handleMarkAll('skip')}>Skip</Dropdown.Item>
99
- </Dropdown.Content>
100
- </Dropdown>
101
- ```
102
-
103
- ---
104
-
105
- ### Student Photo Avatar (`src/components/avatar/Avatar.tsx`)
106
-
107
- Dorothy confirmed `src` works for photos — renders `<img>` with `object-fit: cover`. The three-mode fallback (image → initials → placeholder silhouette) is perfect for a student roster where some kids may not have photos.
108
-
109
- **Dorothy-verified sizes:**
110
- | Size | Pixels |
111
- |---|---|
112
- | `'small'` | 20px |
113
- | `'medium'` | 32px |
114
- | `'large'` | **48px** ← closest to ~50px |
115
- | `'extra-large'` | 96px |
116
-
117
- ```tsx
118
- <Avatar
119
- size="large"
120
- src={student.photoUrl}
121
- alt={student.name}
122
- initials={getInitials(student.name)}
123
- />
124
- ```
125
-
126
- ---
127
-
128
- ### Separators Between Tile Rows (`src/components/separator/Separator.tsx`)
129
-
130
- ```tsx
131
- <Separator orientation="horizontal" decorative />
132
- ```
133
-
134
- ---
135
-
136
- ### Optional Comment / Note Text
137
-
138
- Plain `<p>` or `<span>` with DS tokens. If the design shows it styled distinctly (e.g. "Family Holiday (not agreed)" in amber), use `<Tag color="orange" text="Family Holiday (not agreed)" />`.
139
-
140
- ---
141
-
142
- ### Icon Usage
143
-
144
- Dorothy verified all icon names used in this design:
145
-
146
- | Design element | Icon name | Verified |
147
- |---|---|---|
148
- | Back button | `'arrow-left'` | ✅ line 102 |
149
- | Profile View toggle button | `'user'` | ✅ line 197 |
150
- | List View toggle button | `'list'` | ✅ line 157 |
151
- | Absent mark | `'x'` | ✅ line 198 |
152
- | Present mark | `'check'` | ✅ line 111 |
153
- | Skip mark | `'plus'` | ✅ line 175 |
154
- | Late mark | `'clock-3'` | ✅ line 127 — **`'clock'` DOES NOT EXIST** |
155
- | Dropdown chevron | `'chevron-down'` | ✅ |
156
-
157
- ---
158
-
159
- ### UserDropdown + SearchBar (Top Nav)
160
-
161
- `UserDropdown variant="dark"` + `SearchBar` — already covered in MIS-69959 (TopNav) and confirmed exported.
162
-
163
- ---
164
-
165
- ## Section 2: Almost There — Minor Issues or Gotchas
166
-
167
- ### Segmented Icon Button Group (Profile/List View Toggle)
168
-
169
- **What exists:** `Tabs` component has `Tabs.Item` with `iconName` prop and controlled `active` state. Mutual exclusivity can be managed by the caller. Dorothy confirmed `iconName` prop exists on `TabsItem` (line 7).
170
-
171
- **What doesn't match:** `Tabs` renders as a tab bar with a **brand-color underline indicator** on the active item. The Figma shows a **pill/capsule container** with the active button having a **filled background** — a segmented control visual, not a tab underline. Fighting Tabs CSS with overrides would be fragile.
172
-
173
- **The fix — compose from `Button` primitives:**
174
-
175
- ```tsx
176
- const [viewMode, setViewMode] = useState<'profile' | 'list'>('profile');
177
-
178
- <div className="ds-register__view-toggle">
179
- <Button
180
- variant={viewMode === 'profile' ? 'primary' : 'secondary'}
181
- iconLeftName="user"
182
- iconLeftScreenReaderText="Profile View"
183
- onClick={() => setViewMode('profile')}
184
- aria-pressed={viewMode === 'profile'}
185
- />
186
- <Button
187
- variant={viewMode === 'list' ? 'primary' : 'secondary'}
188
- iconLeftName="list"
189
- iconLeftScreenReaderText="List View"
190
- onClick={() => setViewMode('list')}
191
- aria-pressed={viewMode === 'list'}
192
- />
193
- </div>
194
- ```
195
-
196
- With a small SCSS wrapper giving the container `border-radius: var(--border-radius-round)` and no gap between the buttons. **Page-level composition, not a library gap — for this single page.** If this pattern appears on other pages, extract as a `SegmentedControl` library component.
197
-
198
- ⚠️ **Dorothy's note on Tabs icon-only accessibility:** If you DO use `Tabs.Item` with only `iconName` and no `children`, there is NO `aria-label` automatically applied. You MUST pass one via `tabElementProps={{ 'aria-label': 'Profile View' }}` — the component doesn't handle it for you.
199
-
200
- ---
201
-
202
- ### Breadcrumbs — Composition Only
203
-
204
- Already tracked as **MIS-69957**. Compose from `Button variant="text-link"` + `Separator orientation="vertical"` + semantic `<nav>/<ol>/<li>` HTML.
205
-
206
- ---
207
-
208
- ## Section 3: Build It From Scratch — These Don't Exist Yet
209
-
210
- ### AttendanceMarkSelector — HIGHEST PRIORITY FOR THIS PAGE
211
-
212
- This is the most important gap on the entire Take Register page. The four-button "✕ Absent · ◎ Late · ✓ Present · + Skip" toggle is the PRIMARY interaction and nothing in the library handles it.
213
-
214
- **Why every candidate fails:**
215
-
216
- | Candidate | Why it fails |
217
- |---|---|
218
- | `RadioButtonInput` / `RadioButtonGroup` | Renders as traditional radio circles. No color-coded active states (red/amber/green), no icon support per option, wrong visual entirely |
219
- | `Pill` | **Dorothy confirmed: uncontrolled internal state** — there is no `value` or `checked` prop to control it externally after mount. `onclick` fires on MOUNT too (via `useEffect`). Renders as `<span>` with no keyboard accessibility, no `tabIndex`. Cannot reliably deselect a Pill programmatically. |
220
- | `Tabs` | `role="tab"` is semantically incorrect for data-entry. Tab underline visual is wrong. |
221
- | Raw `Button` group | Could compose visually, but you'd manually wire mutual exclusivity, color variants, and ARIA — at which point you've built a component anyway. |
222
-
223
- **What needs building: `AttendanceMarkSelector`**
224
-
225
- ```tsx
226
- // Proposed API
227
- <AttendanceMarkSelector
228
- value="present" // 'absent' | 'late' | 'present' | 'skip' | null
229
- onChange={(value) => {}} // controlled
230
- disabled={false}
231
- />
232
- ```
233
-
234
- **Requirements:**
235
- - 4 options: Absent (uses `--color-destructive-*`), Late (uses `--color-caution-*`), Present (uses `--color-success-*`), Skip (uses `--color-grey-*`)
236
- - Mutually exclusive — semantic `role="radiogroup"` + `role="radio"` per button
237
- - Each option: icon + text label (`'x'` / `'clock-3'` / `'check'` / `'plus'`)
238
- - Controlled API — `value` prop drives active state, no internal state
239
- - `disabled` prop for read-only marks
240
- - Uses existing semantic color tokens — no new tokens needed
241
-
242
- ⚠️ **Icon trap:** `'clock'` does NOT exist. Use `'clock-3'` (Dorothy verified, line 127).
243
-
244
- **Effort:** Medium. High priority — the page is unusable without it.
245
-
246
- ---
247
-
248
- ### AttendanceHistoryGrid — Supporting Component
249
-
250
- The right-hand panel per register tile showing "Reg P1 P2 P3 P4 P5" column headers with colored mark indicators (green check, red X, amber clock). This is NOT a Table — no sorting, no resize, no AG Grid overhead needed for 6 cells.
251
-
252
- **What needs building:** A purely presentational `AttendanceHistoryGrid` component.
253
-
254
- ```tsx
255
- // Proposed API
256
- <AttendanceHistoryGrid
257
- periods={[
258
- { label: 'Reg', mark: 'present' },
259
- { label: 'P1', mark: 'present' },
260
- { label: 'P2', mark: 'absent' },
261
- { label: 'P3', mark: null }, // unmarked
262
- { label: 'P4', mark: 'late' },
263
- { label: 'P5', mark: 'present' },
264
- ]}
265
- />
266
- ```
267
-
268
- Internally composes from `Icon` with color overrides using semantic color tokens. CSS grid or flex layout. No AG Grid needed.
269
-
270
- **Effort:** Low-Medium. Purely presentational — straightforward to build once `AttendanceMarkSelector` types are defined.
271
-
272
- ---
273
-
274
- ### RegisterTile — Page-Level Composite (NOT a library component)
275
-
276
- The full 1268px × 119px student row composing Avatar + name text + AttendanceMarkSelector + AttendanceHistoryGrid + Separator + optional note. This is too domain-specific for the design system library (it knows about students, attendance marks, period history).
277
-
278
- **Build in the MIS app as `RegisterTile.tsx`.** Compose from:
279
- - `Avatar size="large"` (student photo)
280
- - Plain text (student name)
281
- - `AttendanceMarkSelector` (new component above)
282
- - `AttendanceHistoryGrid` (new component above)
283
- - `Separator`
284
- - Conditional `<p>` / `<Tag>` for notes
285
-
286
- **Effort:** Low once the two new library components exist.
287
-
288
- ---
289
-
290
- ### Top Navigation Bar, Side Navigation, Notification Badge, Breadcrumbs (Library Component)
291
-
292
- Already tracked: MIS-69959, MIS-69960, MIS-69958, MIS-69957 respectively.
293
-
294
- Note: The **collapsed/icon-only variant** of the left nav (58px, icons only) is a different visual state than the 232px expanded variant seen in Student Profile. Whether this is a CSS modifier on the same component or a separate component is a design decision for the SideNavigation build (MIS-69960).
295
-
296
- ---
297
-
298
- ## Section 4: Tricky Bits — Pay Attention Here
299
-
300
- ### 1. `'clock'` Does NOT Exist — Use `'clock-3'`
301
-
302
- Dorothy caught this. There is NO `'clock'` in allowedIcons. The Late attendance mark icon MUST use `'clock-3'` (line 127, maps to Lucide `Clock3`). TypeScript will catch this at compile time — don't ignore the error.
303
-
304
- ### 2. Pill Cannot Do Mutually-Exclusive Selection
305
-
306
- Dorothy's most important finding for this page: `Pill` is **uncontrolled**. Each pill manages its own `useState`. There's no `value` prop, no `checked` prop, no way to deselect a pill from outside the component. `onclick` fires on mount via `useEffect`. Pill renders as a `<span>` — no keyboard accessibility.
307
-
308
- Do NOT use `Pill` for the attendance mark buttons. Build `AttendanceMarkSelector`.
309
-
310
- ### 3. Icon-Only Buttons/Tabs Need `aria-label` — Not Automatic
311
-
312
- Dorothy confirmed: `Tabs.Item` with only `iconName` and no `children` gets no `aria-label` automatically. Pass it via `tabElementProps={{ 'aria-label': 'Profile View' }}`. Same rule applies to icon-only `Button` — always provide `iconLeftScreenReaderText`.
313
-
314
- ### 4. Avatar Has No 50px Size — Use `'large'` (48px)
315
-
316
- No Avatar size hits exactly 50px. `'large'` = 48px is your closest match. Accept it, or apply a `className` override if pixel-perfect is required.
317
-
318
- ### 5. AttendanceMarkSelector Must Be Controlled
319
-
320
- Build it controlled from day one. If you make it uncontrolled (like Pill), the next designer will ask "can we add a Medical mark type?" and you'll have to rewrite it. Controlled API with `value` + `onChange`, token-driven colors per mark type.
321
-
322
- ---
323
-
324
- ## Section 5: The Verdict
325
-
326
- Picture it: Sicily, 1928. My mother took that register with chalk and bare hands. Today, we've got a design system. We've got `Avatar`, `Button`, `Dropdown`, `Separator`, `Heading`, `Icon`. That's your chalk.
327
-
328
- What we're MISSING is the actual register form itself — the attendance mark control. That's the desk that isn't there.
329
-
330
- ### Is It Buildable With Our Existing Components?
331
-
332
- **Partially YES. The chrome and structure are covered. The core interaction — `AttendanceMarkSelector` — needs building first.**
333
-
334
- ### Summary Table
335
-
336
- | Design Element | Library Component | Status |
337
- |---|---|---|
338
- | Back button | `Button variant="secondary" size="S" iconLeftName="arrow-left"` | ✅ Ready |
339
- | "Take Register" H1 | `Heading level={1}` | ✅ Ready |
340
- | Sub-info text | Plain HTML + `Tag` (optional) | ✅ Ready |
341
- | "Mark All Blank As..." | `Dropdown` + `Button variant="primary"` | ✅ Ready |
342
- | Student photo avatar | `Avatar size="large"` (48px) | ✅ Ready |
343
- | Separators between rows | `Separator decorative` | ✅ Ready |
344
- | Comment/note text | Plain HTML + `Tag` (optional) | ✅ Ready |
345
- | Icon names (all 8 verified) | `Icon` — 8/8 confirmed (use `'clock-3'` not `'clock'`) | ✅ Ready |
346
- | UserDropdown / SearchBar (nav) | Already covered | ✅ Ready |
347
- | Segmented Profile/List toggle | Two `Button`s + custom CSS wrapper | 🔧 Minor composition |
348
- | Breadcrumbs | Compose: `Button text-link` + `Separator` | 🔧 Per MIS-69957 |
349
- | Attendance mark buttons (Absent/Late/Present/Skip) | `AttendanceMarkSelector` — does not exist | 🆕 HIGH PRIORITY |
350
- | Attendance history mini-grid | `AttendanceHistoryGrid` — does not exist | 🆕 New component |
351
- | RegisterTile container | Page-level `<article>` composite in MIS app | 🆕 App-level |
352
- | Top navigation bar | MIS-69959 | 🆕 Existing ticket |
353
- | Left nav (collapsed variant) | MIS-69960 (add collapsed state to ticket) | 🆕 Existing ticket |
354
- | Notification badge | MIS-69958 | 🆕 Existing ticket |
355
- | Breadcrumbs library component | MIS-69957 | 🆕 Existing ticket |
356
-
357
- ### What Actually Needs Work
358
-
359
- | Item | Where It Lives | Effort |
360
- |---|---|---|
361
- | `AttendanceMarkSelector` | New design system component | Medium |
362
- | `AttendanceHistoryGrid` | New design system component | Low-Medium |
363
- | `RegisterTile` | Page-level composite in MIS app | Low (after above) |
364
- | Segmented toggle (this page) | Page-level composition | Low |
365
- | Collapsed nav state | Addition to MIS-69960 scope | — |
366
-
367
- ### Summary Verdict
368
-
369
- **9 out of 18 elements** ready with current components. 2 need minor composition. 7 need new work — 4 already have tickets, 2 are new library components, 1 is a page-level composite.
370
-
371
- **The critical path:** Build `AttendanceMarkSelector` first — the page literally cannot function without it. `AttendanceHistoryGrid` is display-only and simpler. `RegisterTile` assembles itself once those two exist.
372
-
373
- **Recommendation:** The structural chrome (heading, back button, dropdowns, avatar, separators) is all covered. The core domain-specific UI needs two focused new components. Build them — and build them controlled and token-driven from day one.
374
-
375
- ---
376
-
377
- ## Appendix: Files Referenced in This Assessment
378
-
379
- *Every file Dorothy read to verify claims. This assessment is grounded in actual source code.*
380
-
381
- - `src/index.ts`
382
- - `src/components/tabs/Tabs.tsx`
383
- - `src/components/tabs/TabsItem.tsx`
384
- - `src/components/pill/Pill.tsx`
385
- - `src/components/avatar/Avatar.tsx`
386
- - `src/components/icon/allowedIcons.tsx`
387
- - `src/components/button/Button.tsx`
388
-
389
- ---
390
-
391
- *Analysis by Sophia Petrillo, Componentspert 👜, fact-checked by Dorothy Zbornak 🔍*
392
- *Powered by Claude Code 🐺*