@oneli8/react 1.0.0-beta.5 → 1.0.0-beta.7

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 (40) hide show
  1. package/README.md +7 -3
  2. package/ai-context.json +118 -3
  3. package/package.json +2 -2
  4. package/src/atoms/NavigationBadge.js +19 -0
  5. package/src/foundations/geometry.js +71 -0
  6. package/src/index.d.ts +214 -0
  7. package/src/index.js +14 -0
  8. package/src/molecules/ChoiceChip.js +101 -0
  9. package/src/molecules/FormMessage.js +33 -0
  10. package/src/molecules/Link.js +31 -0
  11. package/src/molecules/NavigationItem.js +55 -0
  12. package/src/molecules/Select.js +109 -0
  13. package/src/molecules/SelectionOption.js +4 -1
  14. package/src/molecules/TextField.js +174 -0
  15. package/src/molecules/Token.js +49 -0
  16. package/src/organisms/ChoicePicker.js +114 -0
  17. package/src/organisms/Combobox.js +122 -0
  18. package/src/organisms/MultiSelectField.js +138 -0
  19. package/src/organisms/SegmentedControl.js +114 -0
  20. package/src/organisms/SelectionPopup.js +73 -0
  21. package/src/organisms/TabBar.js +68 -0
  22. package/src/organisms/Tabs.js +123 -0
  23. package/src/styles/choice-chip.css +216 -0
  24. package/src/styles/choice-picker.css +100 -0
  25. package/src/styles/combobox.css +42 -0
  26. package/src/styles/form-message.css +48 -0
  27. package/src/styles/gem.css +52 -5
  28. package/src/styles/link.css +116 -0
  29. package/src/styles/multi-select-field.css +78 -0
  30. package/src/styles/navigation-badge.css +37 -0
  31. package/src/styles/navigation-item.css +155 -0
  32. package/src/styles/segmented-control.css +115 -0
  33. package/src/styles/select.css +33 -0
  34. package/src/styles/selection-indicator.css +13 -4
  35. package/src/styles/selection-popup.css +92 -0
  36. package/src/styles/tab-bar.css +47 -0
  37. package/src/styles/tabs.css +55 -0
  38. package/src/styles/text-field.css +268 -0
  39. package/src/styles/token.css +65 -0
  40. package/styles.css +18 -2
@@ -0,0 +1,268 @@
1
+ /* GENERATED from packages/core/src/molecules/text-field/text-field.css by packages/sync/src/build-styles.mjs — do not edit.
2
+ Edit the source in packages/core/src and re-run `npm run sync:styles`. */
3
+ /**
4
+ * Oneli8 · Text Field (MOLECULE)
5
+ * Figma: Text Field / Outline 182:33, Text Field / Filled 186:53,
6
+ * Text Field / Outline / Gem 211:36, Text Field / Filled / Gem 215:226,
7
+ * Text Field / Content 175:2, Form Message 177:13.
8
+ *
9
+ * ANATOMY, read from Figma rather than assumed:
10
+ * root vertical, gap Component / Text Field / Gap / Supporting
11
+ * · Label Row horizontal, gap Component / Text Field / Gap / Label
12
+ * · Label twelve over eighteen, Semibold, Tracking Open, Text Primary
13
+ * · Requirement a star in Feedback Critical Content, Regular weight
14
+ * · Control Stack fixed at Component / Text Field / Height / <size>
15
+ * · Focus Edge shown only while focused, Focus Ring Inner Width
16
+ * · Control gap Inline; inset, radius and boundary per size; surface and border
17
+ * per appearance and condition
18
+ * · Leading Icon Size / Icon / Small
19
+ * · Prefix the content size, Text Secondary
20
+ * · Input fills the row
21
+ * · Suffix the content size, Text Secondary
22
+ * · Trailing twenty four
23
+ * · Instruction twelve over eighteen, Text Secondary
24
+ * · Supporting Region horizontal, space between, gap Message Counter
25
+ * · Form Message its own molecule
26
+ * · Counter twelve over eighteen, Text Secondary
27
+ *
28
+ * CONDITION IS NOT A CLASS. Figma's five Conditions are review evidence. Here
29
+ * they are :hover, [aria-invalid], [readonly] and :disabled on the real input,
30
+ * exactly as the Figma description asks.
31
+ */
32
+
33
+ .ol8-field {
34
+ display: flex;
35
+ flex-direction: column;
36
+ gap: var(--ol8-component-textfield-gap-supporting);
37
+ inline-size: 100%;
38
+
39
+ --_surface: var(--ol8-color-surface-default);
40
+ --_border: var(--ol8-color-border-default);
41
+ --_label: var(--ol8-color-text-primary);
42
+ --_ink: var(--ol8-color-text-primary);
43
+ --_muted: var(--ol8-color-text-secondary);
44
+ }
45
+
46
+ /* ---- Label ------------------------------------------------------------ */
47
+ .ol8-field__label-row {
48
+ display: flex;
49
+ align-items: flex-start;
50
+ gap: var(--ol8-component-textfield-gap-requirement);
51
+ }
52
+ .ol8-field__label {
53
+ color: var(--_label);
54
+ font-family: var(--ol8-font-family-functional);
55
+ font-size: var(--ol8-font-size-012);
56
+ font-weight: var(--ol8-font-weight-semibold);
57
+ line-height: var(--ol8-font-line-018);
58
+ letter-spacing: var(--ol8-font-tracking-open);
59
+ }
60
+ .ol8-field__requirement {
61
+ color: var(--ol8-color-feedbackcritical-content);
62
+ font-family: var(--ol8-font-family-functional);
63
+ font-size: var(--ol8-font-size-012);
64
+ font-weight: var(--ol8-font-weight-regular);
65
+ line-height: var(--ol8-font-line-018);
66
+ }
67
+
68
+ /* ---- Control ---------------------------------------------------------- */
69
+ .ol8-field__control-stack {
70
+ position: relative;
71
+ display: flex;
72
+ block-size: var(--_height);
73
+ }
74
+
75
+ /* The contract asks a coarse pointer adapter to expand the activation region
76
+ for Compact and Standard, which are shorter than the protected minimum,
77
+ without overlapping a neighbouring field. That belongs to a platform adapter:
78
+ an overlay drawn here would sit over the value and take the caret away from
79
+ the person, which is worse than the shortfall it fixes. Not implemented. */
80
+
81
+ .ol8-field__control {
82
+ display: flex;
83
+ align-items: center;
84
+ gap: var(--ol8-component-textfield-gap-inline);
85
+ inline-size: 100%;
86
+ block-size: var(--_height);
87
+ padding-inline: var(--_inset);
88
+ border: var(--ol8-component-textfield-boundary) solid var(--_border);
89
+ border-radius: var(--_radius);
90
+ background: var(--_surface);
91
+ transition-property: border-color, background-color;
92
+ transition-duration: var(--ol8-motion-interaction-hover-duration);
93
+ transition-timing-function: var(--ol8-motion-interaction-hover-timing-function);
94
+ }
95
+
96
+ .ol8-field__leading-icon { flex: none; color: var(--ol8-color-icon-secondary); }
97
+
98
+ .ol8-field__affix {
99
+ flex: none;
100
+ color: var(--_muted);
101
+ font-family: var(--ol8-font-family-functional);
102
+ font-size: var(--_content-size);
103
+ font-weight: var(--ol8-font-weight-regular);
104
+ line-height: var(--_content-line);
105
+ }
106
+
107
+ /* The input carries the value, so it takes the primary ink and hands the
108
+ secondary ink to its placeholder. That is exactly the Content molecule's
109
+ Value and Placeholder types. */
110
+ .ol8-field__input {
111
+ flex: 1 1 auto;
112
+ min-inline-size: 0;
113
+ border: 0;
114
+ padding: 0;
115
+ background: none;
116
+ color: var(--_ink);
117
+ font-family: var(--ol8-font-family-functional);
118
+ font-size: var(--_content-size);
119
+ font-weight: var(--ol8-font-weight-regular);
120
+ line-height: var(--_content-line);
121
+ }
122
+ .ol8-field__input::placeholder { color: var(--_muted); opacity: 1; }
123
+ .ol8-field__input:focus { outline: none; }
124
+
125
+ .ol8-field__trailing-action {
126
+ flex: none;
127
+ display: inline-flex;
128
+ border: 0;
129
+ padding: 0;
130
+ background: none;
131
+ color: var(--ol8-color-icon-secondary);
132
+ cursor: pointer;
133
+ }
134
+ .ol8-field__trailing-action:disabled { color: var(--ol8-color-icon-disabled); cursor: not-allowed; }
135
+
136
+ /* ---- Size ------------------------------------------------------------- */
137
+ .ol8-field[data-ol8-size="compact"] {
138
+ --_height: var(--ol8-component-textfield-height-compact);
139
+ --_inset: var(--ol8-component-textfield-inset-compact);
140
+ --_radius: var(--ol8-component-textfield-radius-compact);
141
+ --_content-size: var(--ol8-font-size-012);
142
+ --_content-line: var(--ol8-font-line-018);
143
+ }
144
+ .ol8-field[data-ol8-size="standard"] {
145
+ --_height: var(--ol8-component-textfield-height-standard);
146
+ --_inset: var(--ol8-component-textfield-inset-standard);
147
+ --_radius: var(--ol8-component-textfield-radius-standard);
148
+ --_content-size: var(--ol8-font-size-016);
149
+ --_content-line: var(--ol8-font-line-024);
150
+ }
151
+ /* Comfortable grows its box and keeps the standard reading size, which is what
152
+ Figma's Content set does at this step. */
153
+ .ol8-field[data-ol8-size="comfortable"] {
154
+ --_height: var(--ol8-component-textfield-height-comfortable);
155
+ --_inset: var(--ol8-component-textfield-inset-comfortable);
156
+ --_radius: var(--ol8-component-textfield-radius-comfortable);
157
+ --_content-size: var(--ol8-font-size-016);
158
+ --_content-line: var(--ol8-font-line-024);
159
+ }
160
+ .ol8-field[data-ol8-size="large"] {
161
+ --_height: var(--ol8-component-textfield-height-large);
162
+ --_inset: var(--ol8-component-textfield-inset-large);
163
+ --_radius: var(--ol8-component-textfield-radius-large);
164
+ --_content-size: var(--ol8-font-size-021);
165
+ --_content-line: var(--ol8-font-line-030);
166
+ }
167
+
168
+ /* ---- Appearance ------------------------------------------------------- */
169
+ .ol8-field[data-ol8-appearance="filled"] {
170
+ --_surface: var(--ol8-color-surface-sunken);
171
+ --_border: var(--ol8-color-border-subtle);
172
+ }
173
+
174
+ /* ---- Condition, carried by the real input ----------------------------- */
175
+ /* Base condition precedence is Disabled, then Read only, then Invalid, then
176
+ Default. Hover is an overlay that renders only above Default or Invalid, and
177
+ only for a fine pointer. */
178
+ @media (hover: hover) and (pointer: fine) {
179
+ .ol8-field:hover:not([data-ol8-disabled]):not([data-ol8-readonly]) .ol8-field__control {
180
+ --_border: var(--ol8-color-border-strong);
181
+ }
182
+ }
183
+
184
+ .ol8-field[data-ol8-readonly] .ol8-field__control { --_border: var(--ol8-color-border-subtle); }
185
+
186
+ /* Focus never suppresses Invalid; the focus edge is added on top of it. */
187
+ .ol8-field[data-ol8-invalid] .ol8-field__control,
188
+ .ol8-field:has(.ol8-field__input[aria-invalid="true"]) .ol8-field__control {
189
+ --_border: var(--ol8-color-feedbackcritical-inlinecontent);
190
+ }
191
+
192
+ .ol8-field[data-ol8-disabled] {
193
+ --_label: var(--ol8-color-text-disabled);
194
+ --_muted: var(--ol8-color-text-disabled);
195
+ --_ink: var(--ol8-color-text-disabled);
196
+ }
197
+ .ol8-field[data-ol8-disabled] .ol8-field__control {
198
+ --_surface: var(--ol8-color-surface-disabled);
199
+ --_border: var(--ol8-color-border-disabled);
200
+ }
201
+ .ol8-field[data-ol8-disabled] .ol8-field__input { cursor: not-allowed; }
202
+ .ol8-field[data-ol8-disabled] .ol8-field__leading-icon { color: var(--ol8-color-icon-disabled); }
203
+
204
+ /* ---- Focus -----------------------------------------------------------
205
+ Figma draws a Focus Edge sibling that is shown only while focused, at the
206
+ Focus Ring Inner Width in the outer focus colour. The stack owns it so the
207
+ edge cannot change the control's own metrics. */
208
+ .ol8-field__control-stack::after {
209
+ content: "";
210
+ position: absolute;
211
+ inset: 0;
212
+ border-radius: var(--_radius);
213
+ box-shadow: inset 0 0 0 var(--ol8-focus-ring-innerwidth) var(--ol8-color-focus-outer);
214
+ opacity: 0;
215
+ pointer-events: none;
216
+ transition-property: opacity;
217
+ transition-duration: var(--ol8-motion-interaction-focus-duration);
218
+ transition-timing-function: var(--ol8-motion-interaction-focus-timing-function);
219
+ }
220
+ .ol8-field:has(.ol8-field__input:focus-visible) .ol8-field__control-stack::after { opacity: 1; }
221
+
222
+ @media (prefers-reduced-motion: reduce) {
223
+ .ol8-field__control,
224
+ .ol8-field__control-stack::after { transition-duration: var(--ol8-motion-duration-instant); }
225
+ }
226
+
227
+ /* ---- Instruction, message and counter --------------------------------- */
228
+ .ol8-field__instruction {
229
+ color: var(--_muted);
230
+ font-family: var(--ol8-font-family-functional);
231
+ font-size: var(--ol8-font-size-012);
232
+ font-weight: var(--ol8-font-weight-regular);
233
+ line-height: var(--ol8-font-line-018);
234
+ letter-spacing: var(--ol8-font-tracking-open);
235
+ }
236
+
237
+ /* The message and the counter share one row, counter at the logical inline end.
238
+ At a narrow width or with enlarged text the counter wraps below the message in
239
+ source order, and the row gap becomes the tighter status step. The counter is
240
+ never hidden merely because a message appeared. */
241
+ .ol8-field__supporting {
242
+ display: flex;
243
+ flex-wrap: wrap;
244
+ align-items: baseline;
245
+ justify-content: space-between;
246
+ column-gap: var(--ol8-component-textfield-gap-messagecounter);
247
+ row-gap: var(--ol8-component-textfield-gap-status);
248
+ }
249
+ .ol8-field__supporting-spacer { flex: 1 1 auto; }
250
+
251
+ /* Counter is information, not validation, so it never takes a feedback colour. */
252
+ .ol8-field__counter {
253
+ flex: none;
254
+ color: var(--_muted);
255
+ font-family: var(--ol8-font-family-functional);
256
+ font-size: var(--ol8-font-size-012);
257
+ font-weight: var(--ol8-font-weight-regular);
258
+ line-height: var(--ol8-font-line-018);
259
+ letter-spacing: var(--ol8-font-tracking-open);
260
+ font-variant-numeric: tabular-nums;
261
+ }
262
+
263
+ @media (forced-colors: active) {
264
+ .ol8-field__control { border-color: ButtonBorder; }
265
+ .ol8-field:has(.ol8-field__input:focus-visible) .ol8-field__control-stack::after {
266
+ box-shadow: inset 0 0 0 var(--ol8-focus-ring-innerwidth) Highlight;
267
+ }
268
+ }
@@ -0,0 +1,65 @@
1
+ /* GENERATED from packages/core/src/molecules/token/token.css by packages/sync/src/build-styles.mjs — do not edit.
2
+ Edit the source in packages/core/src and re-run `npm run sync:styles`. */
3
+ /**
4
+ * Oneli8 · Token (MOLECULE)
5
+ * Figma: Select and Combobox / Molecule / Token 897:2106 (24 variants)
6
+ *
7
+ * The silhouette, sizes and colours all come from the Choice Chip stylesheet,
8
+ * because Figma's Token instantiates the Choice Chip Geometry Owner rather than
9
+ * redrawing it. Every rule here is about the one axis Token adds: the Mark.
10
+ */
11
+
12
+ /* A token is content, not a control. Only its Remove mark is interactive. */
13
+ .ol8-token.ol8-chip { cursor: default; }
14
+
15
+ /* ---- Remove ---------------------------------------------------------- */
16
+ .ol8-token__remove {
17
+ position: relative; /* anchors the expanded target below */
18
+ padding: 0;
19
+ border: 0;
20
+ background: none;
21
+ color: inherit;
22
+ cursor: pointer;
23
+ /* The ring follows the mark, not the chip: the chip is not the target. */
24
+ border-radius: var(--ol8-shape-radius-full);
25
+ }
26
+
27
+ /* The mark is drawn at 18 because that is what Figma draws, but the contract
28
+ asks for a target of at least 24 that does not overlap anything. The target
29
+ is expanded with a pseudo element so the drawn size and the reachable size
30
+ can differ without moving the label: it grows 3 into the 9 gap on one side
31
+ and into the rail inset on the other, so it still touches neither. */
32
+ .ol8-token__remove::before {
33
+ content: "";
34
+ position: absolute;
35
+ inline-size: var(--ol8-component-choice-chip-mark-target);
36
+ block-size: var(--ol8-component-choice-chip-mark-target);
37
+ transform: translate(-50%, -50%);
38
+ inset-block-start: 50%;
39
+ inset-inline-start: 50%;
40
+ }
41
+
42
+ .ol8-token__remove:focus-visible {
43
+ outline: var(--ol8-focus-ring-innerwidth) solid var(--ol8-color-focus-inner);
44
+ outline-offset: var(--ol8-focus-ring-offset);
45
+ }
46
+
47
+ /* ---- Mark size parity ------------------------------------------------
48
+ The two marks have to read as one size, and they did not. The Icon Library's
49
+ cross spans 12 x 12 in the canonical 24 space while the check spans
50
+ 10.2 x 6.9, so drawn at one frame size the cross was fifteen percent wider
51
+ and well over half again as tall, and it swamped the check beside it.
52
+
53
+ Figma does not draw it that way: its Remove mark is an Icon Frame instance at
54
+ 18, so the cross comes out at 9 x 9 there, a little narrower than the check
55
+ and balanced against it. The oversized cross was this stylesheet rendering
56
+ the library source at full size, not a decision in the design.
57
+
58
+ So the cross is scaled to the 9 span Figma draws. The stroke does not follow
59
+ it: it is a non scaling stroke, so it stays the governed 1.8 that every glyph
60
+ in the system uses, which is also what Figma keeps. */
61
+ .ol8-token__remove > svg > * {
62
+ transform: scale(0.75); /* 9 / 12, the span Figma draws */
63
+ transform-box: fill-box;
64
+ transform-origin: center;
65
+ }
package/styles.css CHANGED
@@ -1,13 +1,29 @@
1
- /* OneLi8 · complete stylesheet for @oneli8/react.
1
+ /* GENERATED by packages/sync/src/build-styles.mjs do not edit.
2
+ OneLi8 · complete stylesheet for @oneli8/react.
2
3
  Load order matters: tokens define the custom properties every rule reads,
3
4
  atoms before the molecules that compose them, and the Gem material last
4
5
  because it adapts surfaces the component rules have already painted. */
5
6
  @import "@oneli8/tokens/css";
6
7
  @import "./src/styles/icon.css";
8
+ @import "./src/styles/navigation-badge.css";
7
9
  @import "./src/styles/selection-indicator.css";
8
10
  @import "./src/styles/button.css";
9
- @import "./src/styles/icon-button.css";
11
+ @import "./src/styles/choice-chip.css";
10
12
  @import "./src/styles/choice-item.css";
13
+ @import "./src/styles/form-message.css";
14
+ @import "./src/styles/icon-button.css";
15
+ @import "./src/styles/link.css";
16
+ @import "./src/styles/navigation-item.css";
17
+ @import "./src/styles/select.css";
11
18
  @import "./src/styles/selection-option.css";
19
+ @import "./src/styles/text-field.css";
20
+ @import "./src/styles/token.css";
12
21
  @import "./src/styles/choice-group.css";
22
+ @import "./src/styles/choice-picker.css";
23
+ @import "./src/styles/combobox.css";
24
+ @import "./src/styles/multi-select-field.css";
25
+ @import "./src/styles/segmented-control.css";
26
+ @import "./src/styles/selection-popup.css";
27
+ @import "./src/styles/tab-bar.css";
28
+ @import "./src/styles/tabs.css";
13
29
  @import "./src/styles/gem.css";