@oneli8/core 1.0.0-beta.3
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.
- package/LICENSE +202 -0
- package/NOTICE +8 -0
- package/README.md +76 -0
- package/package.json +79 -0
- package/src/atoms/icon/icon.css +54 -0
- package/src/atoms/icon/icon.d.ts +19 -0
- package/src/atoms/icon/icon.js +81 -0
- package/src/atoms/icon/icons.generated.d.ts +38 -0
- package/src/atoms/icon/icons.generated.js +128 -0
- package/src/atoms/icon/index.d.ts +2 -0
- package/src/atoms/icon/index.js +2 -0
- package/src/atoms/index.d.ts +2 -0
- package/src/atoms/index.js +2 -0
- package/src/atoms/selection-indicator/index.d.ts +1 -0
- package/src/atoms/selection-indicator/index.js +1 -0
- package/src/atoms/selection-indicator/selection-indicator.css +140 -0
- package/src/atoms/selection-indicator/selection-indicator.d.ts +36 -0
- package/src/atoms/selection-indicator/selection-indicator.js +61 -0
- package/src/index.d.ts +3 -0
- package/src/index.js +3 -0
- package/src/materials/gem/gem.css +171 -0
- package/src/molecules/button/button.css +193 -0
- package/src/molecules/button/button.d.ts +51 -0
- package/src/molecules/button/button.js +134 -0
- package/src/molecules/button/index.d.ts +1 -0
- package/src/molecules/button/index.js +1 -0
- package/src/molecules/choice-item/choice-item.css +190 -0
- package/src/molecules/choice-item/choice-item.d.ts +54 -0
- package/src/molecules/choice-item/choice-item.js +153 -0
- package/src/molecules/choice-item/index.d.ts +1 -0
- package/src/molecules/choice-item/index.js +1 -0
- package/src/molecules/icon-button/icon-button.css +222 -0
- package/src/molecules/icon-button/icon-button.d.ts +40 -0
- package/src/molecules/icon-button/icon-button.js +121 -0
- package/src/molecules/icon-button/index.d.ts +1 -0
- package/src/molecules/icon-button/index.js +1 -0
- package/src/molecules/index.d.ts +4 -0
- package/src/molecules/index.js +4 -0
- package/src/molecules/selection-option/index.d.ts +1 -0
- package/src/molecules/selection-option/index.js +1 -0
- package/src/molecules/selection-option/selection-option.css +121 -0
- package/src/molecules/selection-option/selection-option.d.ts +42 -0
- package/src/molecules/selection-option/selection-option.js +97 -0
- package/src/organisms/choice-group/choice-group.css +93 -0
- package/src/organisms/choice-group/choice-group.d.ts +80 -0
- package/src/organisms/choice-group/choice-group.js +159 -0
- package/src/organisms/choice-group/index.d.ts +1 -0
- package/src/organisms/choice-group/index.js +1 -0
- package/src/organisms/index.d.ts +1 -0
- package/src/organisms/index.js +1 -0
- package/styles.css +13 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// GENERATED by packages/sync/src/build-icons.mjs — do not edit by hand.
|
|
2
|
+
// Source: Figma "Oneli8 Design System — 1.0.0-beta.1", page Iconography,
|
|
3
|
+
// frame "Icon Library / Masters" (426:55). Re-run `npm run sync:icons`.
|
|
4
|
+
|
|
5
|
+
export const OL8_ICON_VIEWBOX = '0 0 24 24';
|
|
6
|
+
|
|
7
|
+
/** @type {Record<string, import('./icons.generated.js').Ol8IconEntry>} */
|
|
8
|
+
export const OL8_ICONS = {
|
|
9
|
+
"add": {
|
|
10
|
+
"category": "action",
|
|
11
|
+
"figmaNode": "418:14",
|
|
12
|
+
"mirrorInRTL": false,
|
|
13
|
+
"sourceInk": "#151817",
|
|
14
|
+
"body": "<path id=\"glyph-path\" d=\"M12 5V19M5 12H19\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>"
|
|
15
|
+
},
|
|
16
|
+
"caution": {
|
|
17
|
+
"category": "status",
|
|
18
|
+
"figmaNode": "440:4",
|
|
19
|
+
"mirrorInRTL": false,
|
|
20
|
+
"sourceInk": "#151817",
|
|
21
|
+
"body": "<path id=\"Status Ring\" d=\"M12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21Z\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/><path id=\"Caution Stem\" d=\"M12 7V13\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/><path id=\"Caution Dot\" d=\"M12 17.6C12.5523 17.6 13 17.1523 13 16.6C13 16.0477 12.5523 15.6 12 15.6C11.4477 15.6 11 16.0477 11 16.6C11 17.1523 11.4477 17.6 12 17.6Z\" fill=\"currentColor\"/>"
|
|
22
|
+
},
|
|
23
|
+
"check": {
|
|
24
|
+
"category": "selection",
|
|
25
|
+
"figmaNode": "391:4",
|
|
26
|
+
"mirrorInRTL": false,
|
|
27
|
+
"sourceInk": "#1a2a59",
|
|
28
|
+
"body": "<path id=\"Check Mark\" d=\"M6.9 12.15L10.2 15.45L17.1 8.55\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>"
|
|
29
|
+
},
|
|
30
|
+
"chevron-down": {
|
|
31
|
+
"category": "navigation",
|
|
32
|
+
"figmaNode": "521:106",
|
|
33
|
+
"mirrorInRTL": false,
|
|
34
|
+
"sourceInk": "#151817",
|
|
35
|
+
"body": "<path id=\"glyph-path\" d=\"M5 8.5L12 15.5L19 8.5\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>"
|
|
36
|
+
},
|
|
37
|
+
"chevron-up": {
|
|
38
|
+
"category": "navigation",
|
|
39
|
+
"figmaNode": "521:108",
|
|
40
|
+
"mirrorInRTL": false,
|
|
41
|
+
"sourceInk": "#151817",
|
|
42
|
+
"body": "<path id=\"glyph-path\" d=\"M5 15.5L12 8.5L19 15.5\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>"
|
|
43
|
+
},
|
|
44
|
+
"clear": {
|
|
45
|
+
"category": "action",
|
|
46
|
+
"figmaNode": "557:4",
|
|
47
|
+
"mirrorInRTL": false,
|
|
48
|
+
"sourceInk": "#151817",
|
|
49
|
+
"body": "<path id=\"glyph-path\" d=\"M3 3L17 17M17 3L3 17\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>"
|
|
50
|
+
},
|
|
51
|
+
"close": {
|
|
52
|
+
"category": "action",
|
|
53
|
+
"figmaNode": "894:123",
|
|
54
|
+
"mirrorInRTL": false,
|
|
55
|
+
"sourceInk": "#151817",
|
|
56
|
+
"body": "<path id=\"glyph-path\" d=\"M6 6L18 18M18 6L6 18\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>"
|
|
57
|
+
},
|
|
58
|
+
"critical": {
|
|
59
|
+
"category": "status",
|
|
60
|
+
"figmaNode": "418:18",
|
|
61
|
+
"mirrorInRTL": false,
|
|
62
|
+
"sourceInk": "#151817",
|
|
63
|
+
"body": "<path id=\"Status Ring\" d=\"M12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21Z\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/><path id=\"Critical Mark\" d=\"M9 9L15 15M15 9L9 15\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>"
|
|
64
|
+
},
|
|
65
|
+
"forward": {
|
|
66
|
+
"category": "navigation",
|
|
67
|
+
"figmaNode": "418:16",
|
|
68
|
+
"mirrorInRTL": true,
|
|
69
|
+
"sourceInk": "#151817",
|
|
70
|
+
"body": "<path id=\"artwork\" d=\"M3 12H21M15 18L21 12L15 6\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>"
|
|
71
|
+
},
|
|
72
|
+
"informative": {
|
|
73
|
+
"category": "status",
|
|
74
|
+
"figmaNode": "440:11",
|
|
75
|
+
"mirrorInRTL": false,
|
|
76
|
+
"sourceInk": "#151817",
|
|
77
|
+
"body": "<path id=\"Status Ring\" d=\"M12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21Z\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/><path id=\"Information Stem\" d=\"M12 11V16\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/><path id=\"Information Dot\" d=\"M12 8.6C12.5523 8.6 13 8.15228 13 7.6C13 7.04771 12.5523 6.6 12 6.6C11.4477 6.6 11 7.04771 11 7.6C11 8.15228 11.4477 8.6 12 8.6Z\" fill=\"currentColor\"/>"
|
|
78
|
+
},
|
|
79
|
+
"loading": {
|
|
80
|
+
"category": "status",
|
|
81
|
+
"figmaNode": "440:2",
|
|
82
|
+
"mirrorInRTL": false,
|
|
83
|
+
"sourceInk": "#151817",
|
|
84
|
+
"body": "<path id=\"glyph-path\" d=\"M12 3C14.0823 2.99817 16.1008 3.71843 17.7116 5.03806C19.3223 6.35769 20.4257 8.19503 20.8336 10.237C21.2415 12.2789 20.9287 14.3992 19.9485 16.2363C18.9684 18.0735 17.3815 19.514 15.4583 20.3123C13.5351 21.1106 11.3946 21.2174 9.40156 20.6143C7.40849 20.0113 5.68619 18.7358 4.52813 17.0053C3.37008 15.2747 2.84794 13.1961 3.05068 11.1237C3.25343 9.05131 4.16851 7.11332 5.64 5.64\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>"
|
|
85
|
+
},
|
|
86
|
+
"mixed": {
|
|
87
|
+
"category": "selection",
|
|
88
|
+
"figmaNode": "391:9",
|
|
89
|
+
"mirrorInRTL": false,
|
|
90
|
+
"sourceInk": "#151817",
|
|
91
|
+
"body": "<rect id=\"Mixed Mark\" x=\"7.5\" y=\"11.1\" width=\"9\" height=\"1.8\" rx=\"0.9\" fill=\"currentColor\"/>"
|
|
92
|
+
},
|
|
93
|
+
"positive": {
|
|
94
|
+
"category": "status",
|
|
95
|
+
"figmaNode": "440:8",
|
|
96
|
+
"mirrorInRTL": false,
|
|
97
|
+
"sourceInk": "#151817",
|
|
98
|
+
"body": "<path id=\"Status Ring\" d=\"M12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21Z\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/><path id=\"Positive Mark\" d=\"M7.60002 12.2L10.6 15.2L16.6 9.2\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>"
|
|
99
|
+
},
|
|
100
|
+
"search": {
|
|
101
|
+
"category": "action",
|
|
102
|
+
"figmaNode": "557:2",
|
|
103
|
+
"mirrorInRTL": false,
|
|
104
|
+
"sourceInk": "#151817",
|
|
105
|
+
"body": "<path id=\"glyph-path\" d=\"M15 15L20 20M10 3C6.134 3 3 6.134 3 10C3 13.866 6.134 17 10 17C13.866 17 17 13.866 17 10C17 6.134 13.866 3 10 3Z\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>"
|
|
106
|
+
},
|
|
107
|
+
"selected-dot": {
|
|
108
|
+
"category": "selection",
|
|
109
|
+
"figmaNode": "391:14",
|
|
110
|
+
"mirrorInRTL": false,
|
|
111
|
+
"sourceInk": "#e2e9ff",
|
|
112
|
+
"body": "<circle id=\"Selected Dot\" cx=\"12\" cy=\"12\" r=\"6\" fill=\"currentColor\"/>"
|
|
113
|
+
},
|
|
114
|
+
"visibility-closed": {
|
|
115
|
+
"category": "action",
|
|
116
|
+
"figmaNode": "440:15",
|
|
117
|
+
"mirrorInRTL": false,
|
|
118
|
+
"sourceInk": "#151817",
|
|
119
|
+
"body": "<path id=\"Closed Lid\" d=\"M4.5 10.5C6.75 13.5 9.25 15 12 15C14.75 15 17.25 13.5 19.5 10.5\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/><path id=\"Lashes\" d=\"M6.75 13.05L5.4 15M9.75 14.55L9.3 16.8M14.25 14.55L14.7 16.8M17.25 13.05L18.6 15\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>"
|
|
120
|
+
},
|
|
121
|
+
"visibility-open": {
|
|
122
|
+
"category": "action",
|
|
123
|
+
"figmaNode": "418:21",
|
|
124
|
+
"mirrorInRTL": false,
|
|
125
|
+
"sourceInk": "#151817",
|
|
126
|
+
"body": "<path id=\"Eye Contour\" d=\"M3 12C5.25 8.25 8.25 6.375 12 6.375C15.75 6.375 18.75 8.25 21 12C18.75 15.75 15.75 17.625 12 17.625C8.25 17.625 5.25 15.75 3 12Z\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/><path id=\"Pupil\" d=\"M12 14.625C13.4497 14.625 14.625 13.4497 14.625 12C14.625 10.5503 13.4497 9.375 12 9.375C10.5503 9.375 9.375 10.5503 9.375 12C9.375 13.4497 10.5503 14.625 12 14.625Z\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>"
|
|
127
|
+
}
|
|
128
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './selection-indicator.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './selection-indicator.js';
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Oneli8 · Selection Indicator (ATOM)
|
|
3
|
+
* Figma: Documentation / Choice Controls (246:3)
|
|
4
|
+
* Icon / Selection / Checkbox 365:2417 (12 variants)
|
|
5
|
+
* Icon / Selection / Radio 366:2371 (8)
|
|
6
|
+
* Icon / Selection / Switch 824:1304 (8)
|
|
7
|
+
*
|
|
8
|
+
* Figma names the tier itself — "Documentation / Atom / Selection Indicator" —
|
|
9
|
+
* and points at packages/react/src/atoms/SelectionIndicator.js (kind=switch),
|
|
10
|
+
* so all three kinds are one atom parameterised by kind, not three components.
|
|
11
|
+
*
|
|
12
|
+
* It composes no other component: the marks are drawn from native elements and
|
|
13
|
+
* one inline path, which is what keeps this an atom rather than a molecule.
|
|
14
|
+
*
|
|
15
|
+
* PURELY PRESENTATIONAL. It carries no role and no state of its own — the
|
|
16
|
+
* native <input> inside Choice Item owns semantics. Always aria-hidden.
|
|
17
|
+
*
|
|
18
|
+
* Axes are Figma's: Selection and Availability.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
.ol8-selection {
|
|
22
|
+
--_box: var(--ol8-size-icon-small); /* 18px compact */
|
|
23
|
+
--_radius: var(--ol8-component-checkbox-radius-compact);
|
|
24
|
+
--_dot: var(--ol8-component-radio-dot-compact);
|
|
25
|
+
--_track-w: var(--ol8-component-switch-trackwidth-compact);
|
|
26
|
+
--_track-h: var(--ol8-component-switch-trackheight-compact);
|
|
27
|
+
--_thumb: var(--ol8-component-switch-thumb-compact);
|
|
28
|
+
|
|
29
|
+
--_surface: transparent;
|
|
30
|
+
--_edge: var(--ol8-color-border-strong);
|
|
31
|
+
--_ink: var(--ol8-color-selection-content);
|
|
32
|
+
|
|
33
|
+
box-sizing: border-box;
|
|
34
|
+
display: inline-flex;
|
|
35
|
+
flex: none;
|
|
36
|
+
align-items: center;
|
|
37
|
+
justify-content: center;
|
|
38
|
+
position: relative;
|
|
39
|
+
inline-size: var(--_box);
|
|
40
|
+
block-size: var(--_box);
|
|
41
|
+
background: var(--_surface);
|
|
42
|
+
color: var(--_ink);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.ol8-selection[data-ol8-size="comfortable"] {
|
|
46
|
+
--_box: var(--ol8-size-icon-standard); /* 24px */
|
|
47
|
+
--_radius: var(--ol8-component-checkbox-radius-comfortable);
|
|
48
|
+
--_dot: var(--ol8-component-radio-dot-comfortable);
|
|
49
|
+
--_track-w: var(--ol8-component-switch-trackwidth-comfortable);
|
|
50
|
+
--_track-h: var(--ol8-component-switch-trackheight-comfortable);
|
|
51
|
+
--_thumb: var(--ol8-component-switch-thumb-comfortable);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* ---- Checkbox -------------------------------------------------------- */
|
|
55
|
+
.ol8-selection--checkbox {
|
|
56
|
+
border: var(--ol8-component-checkbox-boundary) solid var(--_edge);
|
|
57
|
+
border-radius: var(--_radius);
|
|
58
|
+
}
|
|
59
|
+
/* Filled states drop the boundary entirely so the mark occupies the whole
|
|
60
|
+
box, exactly as Figma draws it (glyph at 0,0 at full box size). */
|
|
61
|
+
.ol8-selection--checkbox[data-ol8-selection="checked"],
|
|
62
|
+
.ol8-selection--checkbox[data-ol8-selection="mixed"] {
|
|
63
|
+
border-width: 0;
|
|
64
|
+
--_surface: var(--ol8-color-selection-surface);
|
|
65
|
+
}
|
|
66
|
+
.ol8-selection--checkbox[data-ol8-availability="disabled"] { --_edge: var(--ol8-color-border-disabled); }
|
|
67
|
+
.ol8-selection--checkbox[data-ol8-availability="disabled"][data-ol8-selection="checked"],
|
|
68
|
+
.ol8-selection--checkbox[data-ol8-availability="disabled"][data-ol8-selection="mixed"] {
|
|
69
|
+
--_surface: var(--ol8-color-surface-disabled);
|
|
70
|
+
--_ink: var(--ol8-color-icon-disabled);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* The check glyph scales geometrically with the box (18px master is an exact
|
|
74
|
+
0.75x of the 24px one), so a single 24-unit path serves both sizes. */
|
|
75
|
+
.ol8-selection--checkbox > .ol8-selection__mark > svg {
|
|
76
|
+
display: block;
|
|
77
|
+
inline-size: var(--_box);
|
|
78
|
+
block-size: var(--_box);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* The Mixed bar does NOT scale. Both the 18px and 24px masters carry the same
|
|
82
|
+
9 x 1.8px bar — an approved optical exception — so it is a fixed-size rule
|
|
83
|
+
here rather than a scaled glyph. */
|
|
84
|
+
.ol8-selection--checkbox[data-ol8-selection="mixed"] > .ol8-selection__mark {
|
|
85
|
+
inline-size: var(--ol8-component-choice-mixed-bar-width);
|
|
86
|
+
block-size: var(--ol8-component-choice-mixed-bar-height);
|
|
87
|
+
border-radius: calc(var(--ol8-component-choice-mixed-bar-height) / 2);
|
|
88
|
+
background: currentColor;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* ---- Radio ----------------------------------------------------------- */
|
|
92
|
+
.ol8-selection--radio {
|
|
93
|
+
border: var(--ol8-component-radio-boundary) solid var(--_edge);
|
|
94
|
+
border-radius: var(--ol8-shape-radius-full);
|
|
95
|
+
}
|
|
96
|
+
.ol8-selection--radio[data-ol8-selection="selected"] {
|
|
97
|
+
--_edge: var(--ol8-color-selection-surface);
|
|
98
|
+
--_ink: var(--ol8-color-selection-surface);
|
|
99
|
+
}
|
|
100
|
+
.ol8-selection--radio[data-ol8-availability="disabled"] { --_edge: var(--ol8-color-border-disabled); }
|
|
101
|
+
.ol8-selection--radio[data-ol8-availability="disabled"][data-ol8-selection="selected"] {
|
|
102
|
+
--_ink: var(--ol8-color-icon-disabled);
|
|
103
|
+
}
|
|
104
|
+
.ol8-selection--radio > .ol8-selection__mark {
|
|
105
|
+
inline-size: var(--_dot);
|
|
106
|
+
block-size: var(--_dot);
|
|
107
|
+
border-radius: var(--ol8-shape-radius-full);
|
|
108
|
+
background: currentColor;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* ---- Switch ---------------------------------------------------------- */
|
|
112
|
+
.ol8-selection--switch {
|
|
113
|
+
inline-size: var(--_track-w);
|
|
114
|
+
block-size: var(--_track-h);
|
|
115
|
+
border-radius: var(--ol8-shape-radius-full);
|
|
116
|
+
--_surface: var(--ol8-color-surface-sunken);
|
|
117
|
+
justify-content: flex-start;
|
|
118
|
+
}
|
|
119
|
+
.ol8-selection--switch[data-ol8-selection="on"] { --_surface: var(--ol8-color-selection-surface); }
|
|
120
|
+
.ol8-selection--switch[data-ol8-availability="disabled"] { --_surface: var(--ol8-color-surface-disabled); }
|
|
121
|
+
|
|
122
|
+
.ol8-selection--switch > .ol8-selection__mark {
|
|
123
|
+
position: absolute;
|
|
124
|
+
inset-block-start: var(--ol8-component-switch-inset);
|
|
125
|
+
inset-inline-start: var(--ol8-component-switch-inset);
|
|
126
|
+
inline-size: var(--_thumb);
|
|
127
|
+
block-size: var(--_thumb);
|
|
128
|
+
border-radius: var(--ol8-shape-radius-full);
|
|
129
|
+
background: var(--ol8-color-control-thumb);
|
|
130
|
+
display: inline-flex;
|
|
131
|
+
align-items: center;
|
|
132
|
+
justify-content: center;
|
|
133
|
+
overflow: hidden;
|
|
134
|
+
}
|
|
135
|
+
.ol8-selection--switch[data-ol8-selection="on"] > .ol8-selection__mark {
|
|
136
|
+
inset-inline-start: calc(var(--_track-w) - var(--_thumb) - var(--ol8-component-switch-inset));
|
|
137
|
+
}
|
|
138
|
+
/* Optional artwork inside the thumb: 9px compact, 12px comfortable. */
|
|
139
|
+
.ol8-selection--switch .ol8-icon { --_ol8-icon-size: var(--ol8-size-icon-micro); }
|
|
140
|
+
.ol8-selection--switch[data-ol8-size="comfortable"] .ol8-icon { --_ol8-icon-size: var(--ol8-size-icon-compact); }
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Selection Indicator (ATOM) — presentational only. It renders the mark a
|
|
3
|
+
* checkbox, radio or switch shows; it never owns state, role or focus.
|
|
4
|
+
*/
|
|
5
|
+
export type Ol8SelectionKind = 'checkbox' | 'radio' | 'switch';
|
|
6
|
+
export type Ol8SelectionSize = 'compact' | 'comfortable';
|
|
7
|
+
|
|
8
|
+
export type Ol8CheckboxSelection = 'unchecked' | 'checked' | 'mixed';
|
|
9
|
+
export type Ol8RadioSelection = 'unselected' | 'selected';
|
|
10
|
+
export type Ol8SwitchSelection = 'off' | 'on';
|
|
11
|
+
export type Ol8Selection = Ol8CheckboxSelection | Ol8RadioSelection | Ol8SwitchSelection;
|
|
12
|
+
|
|
13
|
+
export declare const OL8_SELECTION_KINDS: readonly Ol8SelectionKind[];
|
|
14
|
+
export declare const OL8_SELECTION_SIZES: readonly Ol8SelectionSize[];
|
|
15
|
+
|
|
16
|
+
/** Figma's Selection axis, per kind. */
|
|
17
|
+
export declare const OL8_SELECTION_STATES: {
|
|
18
|
+
readonly checkbox: readonly Ol8CheckboxSelection[];
|
|
19
|
+
readonly radio: readonly Ol8RadioSelection[];
|
|
20
|
+
readonly switch: readonly Ol8SwitchSelection[];
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export interface Ol8SelectionIndicatorOptions {
|
|
24
|
+
/** Defaults to the kind's first state (unchecked / unselected / off). */
|
|
25
|
+
selection?: Ol8Selection;
|
|
26
|
+
size?: Ol8SelectionSize;
|
|
27
|
+
disabled?: boolean;
|
|
28
|
+
/** Override the glyph. Defaults to the kind's canonical mark. */
|
|
29
|
+
icon?: string;
|
|
30
|
+
className?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export declare function renderSelectionIndicator(
|
|
34
|
+
kind: Ol8SelectionKind,
|
|
35
|
+
options?: Ol8SelectionIndicatorOptions,
|
|
36
|
+
): string;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Oneli8 · Selection Indicator (ATOM) — headless.
|
|
3
|
+
*
|
|
4
|
+
* Presentational only. It renders the mark a checkbox, radio or switch shows;
|
|
5
|
+
* it never owns state, role or focus. The native <input> in Choice Item does.
|
|
6
|
+
*
|
|
7
|
+
* Kept an atom by the project's rule that atoms compose no other component:
|
|
8
|
+
* the check glyph is inlined as a path here rather than mounting the icon
|
|
9
|
+
* component. renderIconSvg returns a markup string, not a component instance.
|
|
10
|
+
*/
|
|
11
|
+
import { renderIconSvg } from '../icon/icon.js';
|
|
12
|
+
|
|
13
|
+
export const OL8_SELECTION_KINDS = ['checkbox', 'radio', 'switch'];
|
|
14
|
+
export const OL8_SELECTION_SIZES = ['compact', 'comfortable'];
|
|
15
|
+
|
|
16
|
+
/** Figma's Selection axis, per kind. */
|
|
17
|
+
export const OL8_SELECTION_STATES = {
|
|
18
|
+
checkbox: ['unchecked', 'checked', 'mixed'],
|
|
19
|
+
radio: ['unselected', 'selected'],
|
|
20
|
+
switch: ['off', 'on'],
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @param {'checkbox'|'radio'|'switch'} kind
|
|
25
|
+
* @param {{selection?:string,size?:string,disabled?:boolean,icon?:string,className?:string}} [options]
|
|
26
|
+
*/
|
|
27
|
+
export function renderSelectionIndicator(kind, options = {}) {
|
|
28
|
+
if (!OL8_SELECTION_KINDS.includes(kind)) throw new Error(`[ol8] unknown selection kind "${kind}"`);
|
|
29
|
+
const states = OL8_SELECTION_STATES[kind];
|
|
30
|
+
const { selection = states[0], size = 'compact', disabled = false, icon, className } = options;
|
|
31
|
+
|
|
32
|
+
if (!states.includes(selection)) {
|
|
33
|
+
throw new Error(`[ol8] "${selection}" is not a ${kind} selection state (${states.join(', ')})`);
|
|
34
|
+
}
|
|
35
|
+
if (!OL8_SELECTION_SIZES.includes(size)) throw new Error(`[ol8] unknown selection size "${size}"`);
|
|
36
|
+
if (icon && kind !== 'switch') throw new Error('[ol8] only the switch thumb carries optional artwork');
|
|
37
|
+
|
|
38
|
+
const attrs = [
|
|
39
|
+
`class="ol8-selection ol8-selection--${kind}${className ? ` ${className}` : ''}"`,
|
|
40
|
+
`data-ol8-size="${size}"`,
|
|
41
|
+
`data-ol8-selection="${selection}"`,
|
|
42
|
+
`data-ol8-availability="${disabled ? 'disabled' : 'enabled'}"`,
|
|
43
|
+
'aria-hidden="true"',
|
|
44
|
+
].join(' ');
|
|
45
|
+
|
|
46
|
+
return `<span ${attrs}>${renderMark(kind, selection, icon)}</span>`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function renderMark(kind, selection, icon) {
|
|
50
|
+
if (kind === 'checkbox') {
|
|
51
|
+
if (selection === 'checked') return `<span class="ol8-selection__mark">${renderIconSvg('check')}</span>`;
|
|
52
|
+
if (selection === 'mixed') return '<span class="ol8-selection__mark"></span>';
|
|
53
|
+
return '';
|
|
54
|
+
}
|
|
55
|
+
if (kind === 'radio') {
|
|
56
|
+
return selection === 'selected' ? '<span class="ol8-selection__mark"></span>' : '';
|
|
57
|
+
}
|
|
58
|
+
// switch: the thumb is always present; artwork inside it is optional
|
|
59
|
+
const art = icon ? `<span class="ol8-icon">${renderIconSvg(icon)}</span>` : '';
|
|
60
|
+
return `<span class="ol8-selection__mark">${art}</span>`;
|
|
61
|
+
}
|
package/src/index.d.ts
ADDED
package/src/index.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Oneli8 · Gem material adapter
|
|
3
|
+
* Figma: section "Gem Button" (1034:9969) — 144 components across
|
|
4
|
+
* Button/Primary/Gem 100:3, Button/Secondary/Gem 100:208,
|
|
5
|
+
* Icon Button ±Circle Primary/Secondary Gem 102:78, 102:151, 119:202, 119:299.
|
|
6
|
+
*
|
|
7
|
+
* From the Figma description (100:3):
|
|
8
|
+
* "Material adapter — NOT a fifth semantic variant. Geometry, meaning,
|
|
9
|
+
* accessibility, protected target, Size, State, label/icon content and
|
|
10
|
+
* native behavior are inherited from the regular molecule. Material changes
|
|
11
|
+
* only the token-bound surface and inner focus edge. Reduced Transparency
|
|
12
|
+
* resolves to the regular opaque equivalent. Quiet and Destructive use the
|
|
13
|
+
* regular material treatment until a distinct Gem treatment is explicitly
|
|
14
|
+
* approved."
|
|
15
|
+
*
|
|
16
|
+
* So Gem is an ATTRIBUTE on an existing button, never a new class:
|
|
17
|
+
* <button class="ol8-btn ol8-btn--primary" data-ol8-material="gem">
|
|
18
|
+
*
|
|
19
|
+
* It adds exactly two painted layers and touches nothing else:
|
|
20
|
+
* ::before the three-stop body + backdrop blur
|
|
21
|
+
* ::after the rim (specular top edge + integrated light rim)
|
|
22
|
+
*
|
|
23
|
+
* THE GRADIENT ANGLE
|
|
24
|
+
* Figma bakes a per-instance angle: 153.1595deg at Standard (83x42),
|
|
25
|
+
* 151.6609 at Comfortable (89x48), 149.2872 at Large (101x60). Those are not
|
|
26
|
+
* three different designs — every one satisfies
|
|
27
|
+
* theta = 90deg + atan(W / H)
|
|
28
|
+
* to four decimal places, which is exactly CSS's `to bottom right` corner
|
|
29
|
+
* keyword (gradient line perpendicular to the other diagonal). Using the
|
|
30
|
+
* keyword reproduces Figma at every size AND at any label width, where a baked
|
|
31
|
+
* degree value would only be correct for the one instance it was measured on.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
.ol8-btn[data-ol8-material="gem"],
|
|
35
|
+
.ol8-icon-btn[data-ol8-material="gem"] > .ol8-icon-btn__surface {
|
|
36
|
+
position: relative;
|
|
37
|
+
isolation: isolate;
|
|
38
|
+
--_rim: var(--ol8-material-control-gem--rim-default);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* The regular opaque background stays underneath on purpose — it is what
|
|
42
|
+
shows through when the gem layer is suppressed for Reduced Transparency. */
|
|
43
|
+
.ol8-btn[data-ol8-material="gem"]::before,
|
|
44
|
+
.ol8-icon-btn[data-ol8-material="gem"] > .ol8-icon-btn__surface::before {
|
|
45
|
+
content: "";
|
|
46
|
+
position: absolute;
|
|
47
|
+
inset: 0;
|
|
48
|
+
z-index: -1;
|
|
49
|
+
border-radius: inherit;
|
|
50
|
+
pointer-events: none;
|
|
51
|
+
backdrop-filter: blur(var(--ol8-material-control-gem--blur));
|
|
52
|
+
-webkit-backdrop-filter: blur(var(--ol8-material-control-gem--blur));
|
|
53
|
+
background-image: linear-gradient(
|
|
54
|
+
to bottom right,
|
|
55
|
+
var(--_g1) var(--ol8-material-control-gem--stop-1),
|
|
56
|
+
var(--_g2) var(--ol8-material-control-gem--stop-2),
|
|
57
|
+
var(--_g3) var(--ol8-material-control-gem--stop-3));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.ol8-btn[data-ol8-material="gem"]::after,
|
|
61
|
+
.ol8-icon-btn[data-ol8-material="gem"] > .ol8-icon-btn__surface::after {
|
|
62
|
+
content: "";
|
|
63
|
+
position: absolute;
|
|
64
|
+
inset: 0;
|
|
65
|
+
border-radius: inherit;
|
|
66
|
+
pointer-events: none;
|
|
67
|
+
box-shadow:
|
|
68
|
+
inset 0 2px 1px 0 var(--ol8-material-control-gem--rim-specular),
|
|
69
|
+
inset 0 0 0 0 var(--_rim);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* ---- Primary · three-stop same-hue body ------------------------------ */
|
|
73
|
+
.ol8-btn--primary[data-ol8-material="gem"],
|
|
74
|
+
.ol8-icon-btn--primary[data-ol8-material="gem"] > .ol8-icon-btn__surface {
|
|
75
|
+
--_g1: var(--ol8-material-control-gem--primary-start);
|
|
76
|
+
--_g2: var(--ol8-material-control-gem--primary-mid);
|
|
77
|
+
--_g3: var(--ol8-material-control-gem--primary-end);
|
|
78
|
+
--_fg: var(--ol8-material-control-gem--primary-content);
|
|
79
|
+
--_ink: var(--ol8-material-control-gem--primary-content);
|
|
80
|
+
}
|
|
81
|
+
.ol8-btn--primary[data-ol8-material="gem"]:hover:not(:disabled):not([data-ol8-loading]),
|
|
82
|
+
.ol8-icon-btn--primary[data-ol8-material="gem"]:hover:not(:disabled):not([data-ol8-loading]) > .ol8-icon-btn__surface {
|
|
83
|
+
--_g1: var(--ol8-material-control-gem--primary-hover-start);
|
|
84
|
+
--_g2: var(--ol8-material-control-gem--primary-hover-mid);
|
|
85
|
+
--_g3: var(--ol8-material-control-gem--primary-hover-end);
|
|
86
|
+
--_rim: var(--ol8-material-control-gem--rim-hover);
|
|
87
|
+
}
|
|
88
|
+
/* Pressed reuses the HOVER stops and adds the cut-edge drop shadow. */
|
|
89
|
+
.ol8-btn--primary[data-ol8-material="gem"]:active:not(:disabled),
|
|
90
|
+
.ol8-btn--primary[data-ol8-material="gem"][aria-pressed="true"]:not(:disabled),
|
|
91
|
+
.ol8-icon-btn--primary[data-ol8-material="gem"]:active:not(:disabled) > .ol8-icon-btn__surface,
|
|
92
|
+
.ol8-icon-btn--primary[data-ol8-material="gem"][aria-pressed="true"]:not(:disabled) > .ol8-icon-btn__surface {
|
|
93
|
+
--_g1: var(--ol8-material-control-gem--primary-hover-start);
|
|
94
|
+
--_g2: var(--ol8-material-control-gem--primary-hover-mid);
|
|
95
|
+
--_g3: var(--ol8-material-control-gem--primary-hover-end);
|
|
96
|
+
--_rim: var(--ol8-material-control-gem--rim-hover);
|
|
97
|
+
}
|
|
98
|
+
.ol8-btn--primary[data-ol8-material="gem"]:active:not(:disabled),
|
|
99
|
+
.ol8-btn--primary[data-ol8-material="gem"][aria-pressed="true"]:not(:disabled) {
|
|
100
|
+
filter: drop-shadow(0 9px 12px var(--ol8-material-gem-color-cutedge-shadow));
|
|
101
|
+
}
|
|
102
|
+
.ol8-btn--primary[data-ol8-material="gem"][data-ol8-loading],
|
|
103
|
+
.ol8-icon-btn--primary[data-ol8-material="gem"][data-ol8-loading] > .ol8-icon-btn__surface {
|
|
104
|
+
--_g1: var(--ol8-material-control-gem--primary-loading-start);
|
|
105
|
+
--_g2: var(--ol8-material-control-gem--primary-loading-mid);
|
|
106
|
+
--_g3: var(--ol8-material-control-gem--primary-loading-end);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* ---- Secondary · transparent body with an integrated light rim -------
|
|
110
|
+
The regular Secondary's 2px boundary is dropped in Gem; the rim replaces
|
|
111
|
+
it. Reduced Transparency restores the boundary along with the opaque body. */
|
|
112
|
+
.ol8-btn--secondary[data-ol8-material="gem"] { --_bw: 0px; }
|
|
113
|
+
.ol8-btn--secondary[data-ol8-material="gem"],
|
|
114
|
+
.ol8-icon-btn--secondary[data-ol8-material="gem"] > .ol8-icon-btn__surface {
|
|
115
|
+
--_g1: var(--ol8-material-control-gem--secondary-high);
|
|
116
|
+
--_g2: var(--ol8-material-control-gem--secondary-mid);
|
|
117
|
+
--_g3: var(--ol8-material-control-gem--secondary-low);
|
|
118
|
+
--_fg: var(--ol8-material-control-gem--secondary-content);
|
|
119
|
+
--_ink: var(--ol8-material-control-gem--secondary-content);
|
|
120
|
+
--_bd: transparent;
|
|
121
|
+
}
|
|
122
|
+
.ol8-btn--secondary[data-ol8-material="gem"]:hover:not(:disabled):not([data-ol8-loading]),
|
|
123
|
+
.ol8-icon-btn--secondary[data-ol8-material="gem"]:hover:not(:disabled):not([data-ol8-loading]) > .ol8-icon-btn__surface {
|
|
124
|
+
--_g1: var(--ol8-material-control-gem--secondary-hover-high);
|
|
125
|
+
--_g2: var(--ol8-material-control-gem--secondary-hover-mid);
|
|
126
|
+
--_g3: var(--ol8-material-control-gem--secondary-hover-low);
|
|
127
|
+
--_rim: var(--ol8-material-control-gem--rim-hover);
|
|
128
|
+
--_fg: var(--ol8-material-control-gem--secondary-content);
|
|
129
|
+
}
|
|
130
|
+
.ol8-btn--secondary[data-ol8-material="gem"]:active:not(:disabled),
|
|
131
|
+
.ol8-btn--secondary[data-ol8-material="gem"][aria-pressed="true"]:not(:disabled),
|
|
132
|
+
.ol8-icon-btn--secondary[data-ol8-material="gem"]:active:not(:disabled) > .ol8-icon-btn__surface {
|
|
133
|
+
--_g1: var(--ol8-material-control-gem--secondary-hover-high);
|
|
134
|
+
--_g2: var(--ol8-material-control-gem--secondary-hover-mid);
|
|
135
|
+
--_g3: var(--ol8-material-control-gem--secondary-hover-low);
|
|
136
|
+
--_rim: var(--ol8-material-control-gem--rim-hover);
|
|
137
|
+
--_fg: var(--ol8-material-control-gem--secondary-content);
|
|
138
|
+
}
|
|
139
|
+
.ol8-btn--secondary[data-ol8-material="gem"][data-ol8-loading],
|
|
140
|
+
.ol8-icon-btn--secondary[data-ol8-material="gem"][data-ol8-loading] > .ol8-icon-btn__surface {
|
|
141
|
+
--_g1: var(--ol8-material-control-gem--secondary-loading-high);
|
|
142
|
+
--_g2: var(--ol8-material-control-gem--secondary-loading-mid);
|
|
143
|
+
--_g3: var(--ol8-material-control-gem--secondary-loading-low);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* Content must sit above the ::before body. */
|
|
147
|
+
.ol8-btn[data-ol8-material="gem"] > *,
|
|
148
|
+
.ol8-icon-btn[data-ol8-material="gem"] .ol8-icon { position: relative; z-index: 0; }
|
|
149
|
+
|
|
150
|
+
/* ---- Disabled inherits the regular opaque treatment ------------------ */
|
|
151
|
+
.ol8-btn[data-ol8-material="gem"]:disabled::before,
|
|
152
|
+
.ol8-btn[data-ol8-material="gem"]:disabled::after,
|
|
153
|
+
.ol8-icon-btn[data-ol8-material="gem"]:disabled > .ol8-icon-btn__surface::before,
|
|
154
|
+
.ol8-icon-btn[data-ol8-material="gem"]:disabled > .ol8-icon-btn__surface::after { display: none; }
|
|
155
|
+
|
|
156
|
+
/* ---- Reduced Transparency -> the regular opaque equivalent -----------
|
|
157
|
+
Explicitly required by the Figma description. Hiding the gem layers
|
|
158
|
+
reveals the regular molecule's own background, which was never removed. */
|
|
159
|
+
@media (prefers-reduced-transparency: reduce) {
|
|
160
|
+
.ol8-btn[data-ol8-material="gem"]::before,
|
|
161
|
+
.ol8-btn[data-ol8-material="gem"]::after,
|
|
162
|
+
.ol8-icon-btn[data-ol8-material="gem"] > .ol8-icon-btn__surface::before,
|
|
163
|
+
.ol8-icon-btn[data-ol8-material="gem"] > .ol8-icon-btn__surface::after { display: none; }
|
|
164
|
+
.ol8-btn--secondary[data-ol8-material="gem"] {
|
|
165
|
+
--_bw: var(--ol8-component-button-secondaryboundary);
|
|
166
|
+
--_bd: var(--ol8-color-actionsecondary-contentdefault);
|
|
167
|
+
--_fg: var(--ol8-color-actionsecondary-contentdefault);
|
|
168
|
+
}
|
|
169
|
+
.ol8-btn--primary[data-ol8-material="gem"] { --_fg: var(--ol8-color-actionprimary-content); }
|
|
170
|
+
.ol8-btn[data-ol8-material="gem"]:active { filter: none; }
|
|
171
|
+
}
|