bemoji 1.0.0-beta.1

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/css/base.css ADDED
@@ -0,0 +1,135 @@
1
+ /**
2
+ * BEMoji Base Styles
3
+ * A minimal, sensible reset + foundation layer.
4
+ *
5
+ * @import 'bemoji/base';
6
+ */
7
+
8
+ *, *::before, *::after {
9
+ box-sizing: border-box;
10
+ }
11
+
12
+ html {
13
+ font-size: 16px;
14
+ -webkit-text-size-adjust: 100%;
15
+ tab-size: 4;
16
+ }
17
+
18
+ body {
19
+ margin: 0;
20
+ font-family:
21
+ system-ui,
22
+ -apple-system,
23
+ BlinkMacSystemFont,
24
+ 'Segoe UI',
25
+ Roboto,
26
+ sans-serif;
27
+ font-size: var(--โœ๏ธ-base, 1rem);
28
+ line-height: var(--๐Ÿ“–-normal, 1.5);
29
+ color: var(--โฌ›, #0d0d0f);
30
+ background-color: var(--โฌœ, #ffffff);
31
+ -webkit-font-smoothing: antialiased;
32
+ -moz-osx-font-smoothing: grayscale;
33
+ }
34
+
35
+ /* Headings */
36
+ h1, h2, h3, h4, h5, h6 {
37
+ margin: 0;
38
+ font-size: inherit;
39
+ font-weight: inherit;
40
+ }
41
+
42
+ /* Lists */
43
+ ol, ul, menu {
44
+ list-style: none;
45
+ margin: 0;
46
+ padding: 0;
47
+ }
48
+
49
+ /* Media */
50
+ img, svg, video, canvas, audio, iframe, embed, object {
51
+ display: block;
52
+ max-width: 100%;
53
+ }
54
+
55
+ img, video {
56
+ height: auto;
57
+ }
58
+
59
+ /* Forms */
60
+ button, input, optgroup, select, textarea {
61
+ font-family: inherit;
62
+ font-size: 100%;
63
+ line-height: inherit;
64
+ color: inherit;
65
+ margin: 0;
66
+ padding: 0;
67
+ }
68
+
69
+ button, select {
70
+ text-transform: none;
71
+ }
72
+
73
+ button, [type='button'], [type='reset'], [type='submit'] {
74
+ -webkit-appearance: button;
75
+ background-color: transparent;
76
+ background-image: none;
77
+ cursor: pointer;
78
+ }
79
+
80
+ input:where([type='text'], [type='email'], [type='url'],
81
+ [type='password'], [type='number'], [type='search'],
82
+ [type='tel'], textarea, select) {
83
+ appearance: none;
84
+ }
85
+
86
+ /* Tables */
87
+ table {
88
+ text-indent: 0;
89
+ border-color: inherit;
90
+ border-collapse: collapse;
91
+ }
92
+
93
+ /* HR */
94
+ hr {
95
+ height: 0;
96
+ color: inherit;
97
+ border-top-width: 1px;
98
+ }
99
+
100
+ /* Paragraphs */
101
+ p { margin: 0; }
102
+
103
+ /* Links */
104
+ a { color: inherit; text-decoration: inherit; }
105
+
106
+ /* Code */
107
+ code, kbd, samp, pre {
108
+ font-family:
109
+ ui-monospace,
110
+ SFMono-Regular,
111
+ Menlo,
112
+ Monaco,
113
+ Consolas,
114
+ monospace;
115
+ font-size: 1em;
116
+ }
117
+
118
+ /* Accessibility */
119
+ :focus-visible {
120
+ outline: 2px solid var(--๐Ÿ”ต, #2563eb);
121
+ outline-offset: 2px;
122
+ }
123
+
124
+ /* Screen-reader only utility */
125
+ .๐Ÿ”‡ {
126
+ position: absolute;
127
+ width: 1px;
128
+ height: 1px;
129
+ padding: 0;
130
+ margin: -1px;
131
+ overflow: hidden;
132
+ clip: rect(0, 0, 0, 0);
133
+ white-space: nowrap;
134
+ border-width: 0;
135
+ }
@@ -0,0 +1,715 @@
1
+ /**
2
+ * BEMoji Component Library
3
+ * 24 production-ready components, all using emoji class names.
4
+ *
5
+ * @import 'bemoji/components';
6
+ *
7
+ * Requires bemoji/tokens to be imported first.
8
+ */
9
+
10
+ /* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
11
+ ๐Ÿ”˜ BUTTON
12
+ โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
13
+
14
+ .๐Ÿ”˜ {
15
+ display: inline-flex;
16
+ align-items: center;
17
+ justify-content: center;
18
+ gap: var(--๐Ÿ“-2);
19
+ padding: var(--๐Ÿ“-2) var(--๐Ÿ“-4);
20
+ font-size: var(--โœ๏ธ-sm);
21
+ font-weight: 500;
22
+ line-height: 1.25;
23
+ border-radius: var(--โญ•-md);
24
+ border: 1.5px solid transparent;
25
+ cursor: pointer;
26
+ text-decoration: none;
27
+ transition: var(--โšก-base);
28
+ white-space: nowrap;
29
+ user-select: none;
30
+ background: var(--๐Ÿฉถ-light);
31
+ color: var(--โšซ);
32
+ }
33
+
34
+ .๐Ÿ”˜:hover { filter: brightness(0.95); }
35
+ .๐Ÿ”˜:active { transform: scale(0.98); }
36
+ .๐Ÿ”˜:focus-visible { outline: 2px solid var(--๐Ÿ”ต); outline-offset: 2px; }
37
+
38
+ /* Sizes */
39
+ .๐Ÿ”˜--๐Ÿ”ฌ { padding: var(--๐Ÿ“-1) var(--๐Ÿ“-2); font-size: var(--โœ๏ธ-xs); }
40
+ .๐Ÿ”˜--๐Ÿค { padding: calc(var(--๐Ÿ“-1) + 2px) var(--๐Ÿ“-3); font-size: var(--โœ๏ธ-sm); }
41
+ .๐Ÿ”˜--๐Ÿ‹๏ธ { padding: var(--๐Ÿ“-3) var(--๐Ÿ“-6); font-size: var(--โœ๏ธ-base); }
42
+ .๐Ÿ”˜--๐Ÿ”๏ธ { padding: var(--๐Ÿ“-4) var(--๐Ÿ“-8); font-size: var(--โœ๏ธ-lg); }
43
+
44
+ /* Semantic variants */
45
+ .๐Ÿ”˜--๐ŸŒŸ { background: var(--๐Ÿ”ต); color: #fff; border-color: var(--๐Ÿ”ต); }
46
+ .๐Ÿ”˜--๐Ÿ”ด { background: var(--๐Ÿ”ด); color: #fff; border-color: var(--๐Ÿ”ด); }
47
+ .๐Ÿ”˜--๐ŸŸข { background: var(--๐ŸŸข); color: #fff; border-color: var(--๐ŸŸข); }
48
+ .๐Ÿ”˜--๐ŸŸก { background: var(--๐ŸŸก); color: #fff; border-color: var(--๐ŸŸก); }
49
+ .๐Ÿ”˜--๐ŸŸฃ { background: var(--๐ŸŸฃ); color: #fff; border-color: var(--๐ŸŸฃ); }
50
+
51
+ /* Ghost / outline */
52
+ .๐Ÿ”˜--๐Ÿ‘๏ธ {
53
+ background: transparent;
54
+ border-color: currentColor;
55
+ color: var(--โšซ);
56
+ }
57
+ .๐Ÿ”˜--๐Ÿ‘๏ธ:hover { background: var(--๐Ÿฉถ-light); }
58
+
59
+ /* Disabled */
60
+ .๐Ÿ”˜--๐Ÿ‘ป,
61
+ .๐Ÿ”˜:disabled {
62
+ opacity: 0.5;
63
+ cursor: not-allowed;
64
+ pointer-events: none;
65
+ }
66
+
67
+ /* Loading */
68
+ .๐Ÿ”˜--โณ {
69
+ cursor: wait;
70
+ opacity: 0.8;
71
+ }
72
+ .๐Ÿ”˜--โณ::after {
73
+ content: '';
74
+ display: inline-block;
75
+ width: 0.875em;
76
+ height: 0.875em;
77
+ border: 2px solid currentColor;
78
+ border-right-color: transparent;
79
+ border-radius: 50%;
80
+ animation: ๐Ÿ”„-spin 0.6s linear infinite;
81
+ margin-left: var(--๐Ÿ“-2);
82
+ }
83
+
84
+ /* Pill shape */
85
+ .๐Ÿ”˜--๐Ÿ’Š { border-radius: var(--โญ•-full); }
86
+
87
+ /* Full width */
88
+ .๐Ÿ”˜--๐ŸŒŠ { width: 100%; }
89
+
90
+ /* Icon only */
91
+ .๐Ÿ”˜--๐ŸŽญ { padding: var(--๐Ÿ“-2); aspect-ratio: 1; }
92
+
93
+
94
+ /* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
95
+ ๐Ÿƒ CARD
96
+ โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
97
+
98
+ .๐Ÿƒ {
99
+ display: flex;
100
+ flex-direction: column;
101
+ border-radius: var(--โญ•-xl);
102
+ overflow: hidden;
103
+ background: var(--โฌœ);
104
+ box-shadow: var(--๐ŸŒ‘-md);
105
+ transition: var(--โšก-slow);
106
+ }
107
+
108
+ .๐Ÿƒ--๐ŸŒŸ { box-shadow: var(--๐ŸŒ‘-xl); }
109
+ .๐Ÿƒ--โ˜๏ธ { box-shadow: var(--๐ŸŒ‘-2xl); }
110
+ .๐Ÿƒ--โฌœ { box-shadow: none; border: 1px solid var(--๐Ÿฉถ-light); }
111
+
112
+ .๐Ÿƒ__๐Ÿ–ผ๏ธ {
113
+ width: 100%;
114
+ overflow: hidden;
115
+ flex-shrink: 0;
116
+ }
117
+
118
+ .๐Ÿƒ__๐Ÿ–ผ๏ธ img {
119
+ width: 100%;
120
+ height: 100%;
121
+ object-fit: cover;
122
+ display: block;
123
+ }
124
+
125
+ .๐Ÿƒ__๐Ÿ–ผ๏ธ--๐ŸŒŸ {
126
+ outline: 3px solid var(--๐Ÿ”ต);
127
+ outline-offset: -3px;
128
+ }
129
+
130
+ .๐Ÿƒ__๐Ÿ“ {
131
+ padding: var(--๐Ÿ“-6);
132
+ flex: 1;
133
+ display: flex;
134
+ flex-direction: column;
135
+ gap: var(--๐Ÿ“-2);
136
+ }
137
+
138
+ .๐Ÿƒ__๐Ÿ”  {
139
+ font-size: var(--โœ๏ธ-xl);
140
+ font-weight: 700;
141
+ line-height: var(--๐Ÿ“–-tight);
142
+ color: var(--โšซ);
143
+ }
144
+
145
+ .๐Ÿƒ__๐Ÿ“– {
146
+ font-size: var(--โœ๏ธ-sm);
147
+ color: var(--๐Ÿฉถ);
148
+ line-height: var(--๐Ÿ“–-relaxed);
149
+ flex: 1;
150
+ }
151
+
152
+ .๐Ÿƒ__๐Ÿฆถ {
153
+ padding: var(--๐Ÿ“-4) var(--๐Ÿ“-6);
154
+ border-top: 1px solid var(--๐Ÿฉถ-light);
155
+ display: flex;
156
+ align-items: center;
157
+ gap: var(--๐Ÿ“-3);
158
+ }
159
+
160
+ .๐Ÿƒ__๐Ÿท๏ธ { margin-left: auto; }
161
+
162
+
163
+ /* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
164
+ ๐Ÿท๏ธ BADGE
165
+ โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
166
+
167
+ .๐Ÿท๏ธ {
168
+ display: inline-flex;
169
+ align-items: center;
170
+ gap: var(--๐Ÿ“-1);
171
+ padding: var(--๐Ÿ“-1) var(--๐Ÿ“-2\\.5);
172
+ font-size: var(--โœ๏ธ-xs);
173
+ font-weight: 600;
174
+ line-height: 1;
175
+ border-radius: var(--โญ•-full);
176
+ background: var(--๐Ÿฉถ-light);
177
+ color: var(--๐Ÿฉถ-dark);
178
+ white-space: nowrap;
179
+ }
180
+
181
+ .๐Ÿท๏ธ--๐ŸŒŸ { background: var(--๐Ÿ”ต-light); color: var(--๐Ÿ”ต-dark); }
182
+ .๐Ÿท๏ธ--๐Ÿ”ด { background: var(--๐Ÿ”ด-light); color: var(--๐Ÿ”ด-dark); }
183
+ .๐Ÿท๏ธ--๐ŸŸข { background: var(--๐ŸŸข-light); color: var(--๐ŸŸข-dark); }
184
+ .๐Ÿท๏ธ--๐ŸŸก { background: var(--๐ŸŸก-light); color: var(--๐ŸŸก-dark); }
185
+ .๐Ÿท๏ธ--๐Ÿ”ต { background: var(--๐Ÿ”ต-light); color: var(--๐Ÿ”ต-dark); }
186
+ .๐Ÿท๏ธ--๐ŸŸฃ { background: var(--๐ŸŸฃ-light); color: var(--๐ŸŸฃ-dark); }
187
+ .๐Ÿท๏ธ--๐Ÿ”ฅ { background: #fff3e0; color: #e65100; }
188
+ .๐Ÿท๏ธ--๐Ÿ’Ž { background: linear-gradient(135deg, #667eea, #764ba2); color: #fff; }
189
+ .๐Ÿท๏ธ--๐Ÿ†• { background: var(--๐ŸŸข-light); color: var(--๐ŸŸข-dark); }
190
+
191
+
192
+ /* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
193
+ ๐Ÿ”” ALERT
194
+ โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
195
+
196
+ .๐Ÿ”” {
197
+ display: flex;
198
+ gap: var(--๐Ÿ“-3);
199
+ align-items: flex-start;
200
+ padding: var(--๐Ÿ“-4);
201
+ border-radius: var(--โญ•-lg);
202
+ border: 1px solid transparent;
203
+ font-size: var(--โœ๏ธ-sm);
204
+ background: var(--๐Ÿฉถ-light);
205
+ color: var(--โšซ);
206
+ }
207
+
208
+ .๐Ÿ””__๐ŸŽญ { flex-shrink: 0; font-size: 1.1em; }
209
+ .๐Ÿ””__๐Ÿ“ { flex: 1; }
210
+ .๐Ÿ””__๐Ÿ”  { font-weight: 600; margin-bottom: var(--๐Ÿ“-1); }
211
+ .๐Ÿ””__โœ–๏ธ { margin-left: auto; cursor: pointer; opacity: 0.6; flex-shrink: 0; }
212
+ .๐Ÿ””__โœ–๏ธ:hover { opacity: 1; }
213
+
214
+ .๐Ÿ””--๐ŸŒŸ { background: var(--๐Ÿ”ต-light); border-color: var(--๐Ÿ”ต); color: var(--๐Ÿ”ต-dark); }
215
+ .๐Ÿ””--๐Ÿ”ด { background: var(--๐Ÿ”ด-light); border-color: var(--๐Ÿ”ด); color: var(--๐Ÿ”ด-dark); }
216
+ .๐Ÿ””--๐ŸŸข { background: var(--๐ŸŸข-light); border-color: var(--๐ŸŸข); color: var(--๐ŸŸข-dark); }
217
+ .๐Ÿ””--๐ŸŸก { background: var(--๐ŸŸก-light); border-color: var(--๐ŸŸก); color: var(--๐ŸŸก-dark); }
218
+
219
+
220
+ /* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
221
+ ๐Ÿ“‹ FORM
222
+ โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
223
+
224
+ .๐Ÿ“‹ {
225
+ display: flex;
226
+ flex-direction: column;
227
+ gap: var(--๐Ÿ“-6);
228
+ }
229
+
230
+ .๐Ÿ“‹__๐Ÿ“„ {
231
+ display: flex;
232
+ flex-direction: column;
233
+ gap: var(--๐Ÿ“-1\\.5);
234
+ }
235
+
236
+ .๐Ÿ“‹__๐Ÿท๏ธ {
237
+ font-size: var(--โœ๏ธ-sm);
238
+ font-weight: 500;
239
+ color: var(--โšซ);
240
+ display: flex;
241
+ align-items: center;
242
+ gap: var(--๐Ÿ“-1);
243
+ }
244
+
245
+ .๐Ÿ“‹__โ— {
246
+ color: var(--๐Ÿ”ด);
247
+ font-size: var(--โœ๏ธ-xs);
248
+ }
249
+
250
+ /* ๐Ÿ“ฅ INPUT */
251
+ .๐Ÿ“ฅ {
252
+ display: block;
253
+ width: 100%;
254
+ padding: var(--๐Ÿ“-2) var(--๐Ÿ“-3);
255
+ font-size: var(--โœ๏ธ-sm);
256
+ line-height: 1.5;
257
+ color: var(--โšซ);
258
+ background: var(--โฌœ);
259
+ border: 1.5px solid #d1d5db;
260
+ border-radius: var(--โญ•-md);
261
+ transition: var(--โšก-base);
262
+ appearance: none;
263
+ }
264
+
265
+ .๐Ÿ“ฅ:hover { border-color: #9ca3af; }
266
+ .๐Ÿ“ฅ:focus { outline: none; border-color: var(--๐Ÿ”ต); box-shadow: 0 0 0 3px rgba(37,99,235,.15); }
267
+
268
+ .๐Ÿ“ฅ--๐ŸŸข { border-color: var(--๐ŸŸข); }
269
+ .๐Ÿ“ฅ--๐ŸŸข:focus { box-shadow: 0 0 0 3px rgba(22,163,74,.15); }
270
+ .๐Ÿ“ฅ--๐Ÿ”ด { border-color: var(--๐Ÿ”ด); }
271
+ .๐Ÿ“ฅ--๐Ÿ”ด:focus { box-shadow: 0 0 0 3px rgba(220,38,38,.15); }
272
+ .๐Ÿ“ฅ--๐Ÿ‘ป { opacity: 0.5; cursor: not-allowed; background: var(--๐Ÿฉถ-light); }
273
+
274
+ .๐Ÿ“‹__๐Ÿ’ก {
275
+ font-size: var(--โœ๏ธ-xs);
276
+ color: var(--๐Ÿฉถ);
277
+ }
278
+
279
+ .๐Ÿ“‹__๐Ÿšจ {
280
+ font-size: var(--โœ๏ธ-xs);
281
+ color: var(--๐Ÿ”ด);
282
+ display: flex;
283
+ align-items: center;
284
+ gap: var(--๐Ÿ“-1);
285
+ }
286
+
287
+
288
+ /* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
289
+ ๐Ÿงญ NAVBAR
290
+ โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
291
+
292
+ .๐Ÿงญ {
293
+ display: flex;
294
+ align-items: center;
295
+ padding: 0 var(--๐Ÿ“-6);
296
+ height: 64px;
297
+ background: var(--โฌœ);
298
+ border-bottom: 1px solid var(--๐Ÿฉถ-light);
299
+ box-shadow: var(--๐ŸŒ‘-sm);
300
+ position: relative;
301
+ z-index: var(--๐Ÿ”๏ธ-10);
302
+ }
303
+
304
+ .๐Ÿงญ--๐Ÿ•ถ๏ธ {
305
+ background: var(--โฌ›);
306
+ border-color: rgba(255,255,255,0.1);
307
+ color: #fff;
308
+ }
309
+
310
+ .๐Ÿงญ__๐Ÿ”  {
311
+ font-size: var(--โœ๏ธ-xl);
312
+ font-weight: 800;
313
+ letter-spacing: -0.025em;
314
+ text-decoration: none;
315
+ color: inherit;
316
+ }
317
+
318
+ .๐Ÿงญ__๐Ÿ“ {
319
+ display: flex;
320
+ align-items: center;
321
+ gap: var(--๐Ÿ“-1);
322
+ margin-left: var(--๐Ÿ“-8);
323
+ flex: 1;
324
+ }
325
+
326
+ .๐Ÿงญ__๐Ÿ”— {
327
+ display: inline-flex;
328
+ align-items: center;
329
+ padding: var(--๐Ÿ“-2) var(--๐Ÿ“-3);
330
+ font-size: var(--โœ๏ธ-sm);
331
+ font-weight: 500;
332
+ text-decoration: none;
333
+ color: var(--๐Ÿฉถ-dark);
334
+ border-radius: var(--โญ•-md);
335
+ transition: var(--โšก-base);
336
+ }
337
+ .๐Ÿงญ__๐Ÿ”—:hover { background: var(--๐Ÿฉถ-light); color: var(--โšซ); }
338
+ .๐Ÿงญ__๐Ÿ”—--โœ… { background: var(--๐Ÿ”ต-light); color: var(--๐Ÿ”ต); }
339
+
340
+ .๐Ÿงญ__โšก {
341
+ margin-left: auto;
342
+ display: flex;
343
+ align-items: center;
344
+ gap: var(--๐Ÿ“-3);
345
+ }
346
+
347
+
348
+ /* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
349
+ ๐ŸชŸ MODAL
350
+ โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
351
+
352
+ .๐ŸชŸ {
353
+ position: fixed;
354
+ inset: 0;
355
+ z-index: var(--๐Ÿ”๏ธ-modal);
356
+ display: flex;
357
+ align-items: center;
358
+ justify-content: center;
359
+ padding: var(--๐Ÿ“-6);
360
+ }
361
+
362
+ .๐ŸชŸ__๐ŸŒซ๏ธ {
363
+ position: absolute;
364
+ inset: 0;
365
+ background: rgba(0,0,0,0.5);
366
+ backdrop-filter: blur(2px);
367
+ }
368
+
369
+ .๐ŸชŸ__๐Ÿ“ {
370
+ position: relative;
371
+ background: var(--โฌœ);
372
+ border-radius: var(--โญ•-2xl);
373
+ box-shadow: var(--๐ŸŒ‘-2xl);
374
+ max-width: 540px;
375
+ width: 100%;
376
+ max-height: 90vh;
377
+ overflow-y: auto;
378
+ }
379
+
380
+ .๐ŸชŸ__๐Ÿช– {
381
+ display: flex;
382
+ align-items: center;
383
+ justify-content: space-between;
384
+ padding: var(--๐Ÿ“-6);
385
+ border-bottom: 1px solid var(--๐Ÿฉถ-light);
386
+ }
387
+
388
+ .๐ŸชŸ__๐Ÿ”  {
389
+ font-size: var(--โœ๏ธ-xl);
390
+ font-weight: 700;
391
+ }
392
+
393
+ .๐ŸชŸ__โœ–๏ธ {
394
+ cursor: pointer;
395
+ opacity: 0.5;
396
+ transition: var(--โšก-base);
397
+ font-size: 1.25rem;
398
+ background: none;
399
+ border: none;
400
+ padding: var(--๐Ÿ“-1);
401
+ }
402
+ .๐ŸชŸ__โœ–๏ธ:hover { opacity: 1; }
403
+
404
+ .๐ŸชŸ__๐Ÿ“– {
405
+ padding: var(--๐Ÿ“-6);
406
+ }
407
+
408
+ .๐ŸชŸ__๐Ÿฆถ {
409
+ padding: var(--๐Ÿ“-4) var(--๐Ÿ“-6);
410
+ border-top: 1px solid var(--๐Ÿฉถ-light);
411
+ display: flex;
412
+ justify-content: flex-end;
413
+ gap: var(--๐Ÿ“-3);
414
+ }
415
+
416
+
417
+ /* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
418
+ ๐Ÿ“‘ TABS
419
+ โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
420
+
421
+ .๐Ÿ“‘ { display: flex; flex-direction: column; }
422
+
423
+ .๐Ÿ“‘__๐Ÿช– {
424
+ display: flex;
425
+ border-bottom: 1px solid var(--๐Ÿฉถ-light);
426
+ overflow-x: auto;
427
+ }
428
+
429
+ .๐Ÿ“‘__๐Ÿ”˜ {
430
+ display: inline-flex;
431
+ align-items: center;
432
+ gap: var(--๐Ÿ“-2);
433
+ padding: var(--๐Ÿ“-3) var(--๐Ÿ“-4);
434
+ font-size: var(--โœ๏ธ-sm);
435
+ font-weight: 500;
436
+ color: var(--๐Ÿฉถ-dark);
437
+ border-bottom: 2px solid transparent;
438
+ cursor: pointer;
439
+ transition: var(--โšก-base);
440
+ white-space: nowrap;
441
+ background: none;
442
+ border-top: none;
443
+ border-left: none;
444
+ border-right: none;
445
+ margin-bottom: -1px;
446
+ }
447
+
448
+ .๐Ÿ“‘__๐Ÿ”˜:hover { color: var(--โšซ); }
449
+ .๐Ÿ“‘__๐Ÿ”˜--โœ… { color: var(--๐Ÿ”ต); border-bottom-color: var(--๐Ÿ”ต); }
450
+
451
+ .๐Ÿ“‘__๐Ÿ“ { padding: var(--๐Ÿ“-6) 0; }
452
+
453
+ .๐Ÿ“‘__๐Ÿ“„ { display: none; }
454
+ .๐Ÿ“‘__๐Ÿ“„--โœ… { display: block; }
455
+
456
+
457
+ /* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
458
+ ๐Ÿ’ฌ TOOLTIP
459
+ โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
460
+
461
+ .๐Ÿ’ฌ {
462
+ position: relative;
463
+ display: inline-block;
464
+ }
465
+
466
+ .๐Ÿ’ฌ__๐Ÿ“ {
467
+ position: absolute;
468
+ bottom: calc(100% + 8px);
469
+ left: 50%;
470
+ transform: translateX(-50%);
471
+ background: var(--โšซ);
472
+ color: #fff;
473
+ font-size: var(--โœ๏ธ-xs);
474
+ padding: var(--๐Ÿ“-1\\.5) var(--๐Ÿ“-2\\.5);
475
+ border-radius: var(--โญ•-md);
476
+ white-space: nowrap;
477
+ pointer-events: none;
478
+ opacity: 0;
479
+ transition: opacity 0.15s ease;
480
+ z-index: var(--๐Ÿ”๏ธ-tooltip);
481
+ }
482
+
483
+ .๐Ÿ’ฌ__๐Ÿ“::after {
484
+ content: '';
485
+ position: absolute;
486
+ top: 100%;
487
+ left: 50%;
488
+ transform: translateX(-50%);
489
+ border: 5px solid transparent;
490
+ border-top-color: var(--โšซ);
491
+ }
492
+
493
+ .๐Ÿ’ฌ:hover .๐Ÿ’ฌ__๐Ÿ“ { opacity: 1; }
494
+
495
+ .๐Ÿ’ฌ--๐Ÿ”ด .๐Ÿ’ฌ__๐Ÿ“ { background: var(--๐Ÿ”ด); }
496
+ .๐Ÿ’ฌ--๐Ÿ”ด .๐Ÿ’ฌ__๐Ÿ“::after { border-top-color: var(--๐Ÿ”ด); }
497
+
498
+
499
+ /* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
500
+ ๐Ÿž BREADCRUMB
501
+ โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
502
+
503
+ .๐Ÿž {
504
+ display: flex;
505
+ align-items: center;
506
+ flex-wrap: wrap;
507
+ gap: var(--๐Ÿ“-1);
508
+ font-size: var(--โœ๏ธ-sm);
509
+ }
510
+
511
+ .๐Ÿž__๐Ÿ“„ {
512
+ display: flex;
513
+ align-items: center;
514
+ gap: var(--๐Ÿ“-1);
515
+ color: var(--๐Ÿฉถ);
516
+ }
517
+
518
+ .๐Ÿž__๐Ÿ“„::after {
519
+ content: '/';
520
+ opacity: 0.4;
521
+ }
522
+
523
+ .๐Ÿž__๐Ÿ“„:last-child { color: var(--โšซ); font-weight: 500; }
524
+ .๐Ÿž__๐Ÿ“„:last-child::after { content: none; }
525
+
526
+ .๐Ÿž__๐Ÿ”— {
527
+ text-decoration: none;
528
+ color: inherit;
529
+ transition: var(--โšก-base);
530
+ }
531
+ .๐Ÿž__๐Ÿ”—:hover { color: var(--๐Ÿ”ต); }
532
+
533
+
534
+ /* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
535
+ ๐Ÿ“ถ PROGRESS
536
+ โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
537
+
538
+ .๐Ÿ“ถ {
539
+ display: flex;
540
+ flex-direction: column;
541
+ gap: var(--๐Ÿ“-1\\.5);
542
+ }
543
+
544
+ .๐Ÿ“ถ__๐Ÿ”  {
545
+ display: flex;
546
+ justify-content: space-between;
547
+ font-size: var(--โœ๏ธ-sm);
548
+ font-weight: 500;
549
+ }
550
+
551
+ .๐Ÿ“ถ__๐Ÿ–ผ๏ธ {
552
+ height: 8px;
553
+ background: var(--๐Ÿฉถ-light);
554
+ border-radius: var(--โญ•-full);
555
+ overflow: hidden;
556
+ }
557
+
558
+ .๐Ÿ“ถ__๐Ÿ“ {
559
+ height: 100%;
560
+ background: var(--๐Ÿ”ต);
561
+ border-radius: var(--โญ•-full);
562
+ transition: width 0.3s ease;
563
+ }
564
+
565
+ .๐Ÿ“ถ--๐Ÿ”ด .๐Ÿ“ถ__๐Ÿ“ { background: var(--๐Ÿ”ด); }
566
+ .๐Ÿ“ถ--๐ŸŸข .๐Ÿ“ถ__๐Ÿ“ { background: var(--๐ŸŸข); }
567
+ .๐Ÿ“ถ--๐ŸŸก .๐Ÿ“ถ__๐Ÿ“ { background: var(--๐ŸŸก); }
568
+ .๐Ÿ“ถ--๐ŸŸฃ .๐Ÿ“ถ__๐Ÿ“ { background: var(--๐ŸŸฃ); }
569
+
570
+
571
+ /* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
572
+ ๐Ÿ‘ค AVATAR
573
+ โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
574
+
575
+ .๐Ÿ‘ค {
576
+ position: relative;
577
+ display: inline-flex;
578
+ align-items: center;
579
+ justify-content: center;
580
+ width: 40px;
581
+ height: 40px;
582
+ border-radius: 50%;
583
+ background: var(--๐Ÿ”ต-light);
584
+ color: var(--๐Ÿ”ต);
585
+ font-weight: 700;
586
+ font-size: var(--โœ๏ธ-sm);
587
+ overflow: hidden;
588
+ flex-shrink: 0;
589
+ }
590
+
591
+ .๐Ÿ‘ค--๐Ÿ”ฌ { width: 24px; height: 24px; font-size: var(--โœ๏ธ-xs); }
592
+ .๐Ÿ‘ค--๐Ÿค { width: 32px; height: 32px; font-size: var(--โœ๏ธ-xs); }
593
+ .๐Ÿ‘ค--๐Ÿ‹๏ธ { width: 48px; height: 48px; font-size: var(--โœ๏ธ-base); }
594
+ .๐Ÿ‘ค--๐Ÿ”๏ธ { width: 64px; height: 64px; font-size: var(--โœ๏ธ-lg); }
595
+ .๐Ÿ‘ค--๐ŸŒ { width: 80px; height: 80px; font-size: var(--โœ๏ธ-xl); }
596
+
597
+ .๐Ÿ‘ค img { width: 100%; height: 100%; object-fit: cover; }
598
+
599
+ .๐Ÿ‘ค__๐Ÿ”ข {
600
+ position: absolute;
601
+ bottom: 0;
602
+ right: 0;
603
+ width: 10px;
604
+ height: 10px;
605
+ border-radius: 50%;
606
+ background: var(--๐ŸŸข);
607
+ border: 2px solid white;
608
+ }
609
+
610
+ .๐Ÿ‘ค__๐Ÿ”ข--๐Ÿ”ด { background: var(--๐Ÿ”ด); }
611
+ .๐Ÿ‘ค__๐Ÿ”ข--๐ŸŸก { background: var(--๐ŸŸก); }
612
+ .๐Ÿ‘ค__๐Ÿ”ข--โฌœ { background: var(--๐Ÿฉถ); }
613
+
614
+
615
+ /* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
616
+ ๐Ÿ“Š TABLE
617
+ โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
618
+
619
+ .๐Ÿ“Š {
620
+ width: 100%;
621
+ border-collapse: collapse;
622
+ font-size: var(--โœ๏ธ-sm);
623
+ }
624
+
625
+ .๐Ÿ“Š__๐Ÿช– { }
626
+
627
+ .๐Ÿ“Š__๐Ÿ”  {
628
+ text-align: left;
629
+ padding: var(--๐Ÿ“-3) var(--๐Ÿ“-4);
630
+ font-weight: 600;
631
+ color: var(--๐Ÿฉถ-dark);
632
+ font-size: var(--โœ๏ธ-xs);
633
+ letter-spacing: 0.05em;
634
+ text-transform: uppercase;
635
+ border-bottom: 2px solid var(--๐Ÿฉถ-light);
636
+ }
637
+
638
+ .๐Ÿ“Š__๐Ÿ“„ { border-bottom: 1px solid var(--๐Ÿฉถ-light); transition: var(--โšก-fast); }
639
+ .๐Ÿ“Š__๐Ÿ“„:hover { background: var(--๐Ÿฉถ-light); }
640
+ .๐Ÿ“Š__๐Ÿ“„--โœ… { background: var(--๐Ÿ”ต-light); }
641
+
642
+ .๐Ÿ“Š__๐Ÿ“ {
643
+ padding: var(--๐Ÿ“-3) var(--๐Ÿ“-4);
644
+ color: var(--โšซ);
645
+ vertical-align: middle;
646
+ }
647
+
648
+ .๐Ÿ“Š--โฌœ { border: 1px solid var(--๐Ÿฉถ-light); border-radius: var(--โญ•-lg); overflow: hidden; }
649
+ .๐Ÿ“Š--โฌœ .๐Ÿ“Š__๐Ÿ”  { background: var(--๐Ÿฉถ-light); }
650
+
651
+
652
+ /* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
653
+ ๐Ÿ“– PAGINATION
654
+ โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
655
+
656
+ .๐Ÿ“– {
657
+ display: flex;
658
+ align-items: center;
659
+ gap: var(--๐Ÿ“-1);
660
+ }
661
+
662
+ .๐Ÿ“–__๐Ÿ”˜ {
663
+ display: inline-flex;
664
+ align-items: center;
665
+ justify-content: center;
666
+ width: 36px;
667
+ height: 36px;
668
+ border-radius: var(--โญ•-md);
669
+ font-size: var(--โœ๏ธ-sm);
670
+ font-weight: 500;
671
+ background: none;
672
+ border: 1.5px solid transparent;
673
+ cursor: pointer;
674
+ color: var(--๐Ÿฉถ-dark);
675
+ transition: var(--โšก-base);
676
+ }
677
+
678
+ .๐Ÿ“–__๐Ÿ”˜:hover { background: var(--๐Ÿฉถ-light); color: var(--โšซ); }
679
+ .๐Ÿ“–__๐Ÿ”˜--โœ… { background: var(--๐Ÿ”ต); color: #fff; border-color: var(--๐Ÿ”ต); }
680
+ .๐Ÿ“–__๐Ÿ”˜--๐Ÿ‘ป { opacity: 0.4; cursor: not-allowed; }
681
+
682
+
683
+ /* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
684
+ ๐Ÿ’€ SKELETON (loading placeholder)
685
+ โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
686
+
687
+ @keyframes ๐Ÿ’€-pulse {
688
+ 0%, 100% { opacity: 1; }
689
+ 50% { opacity: 0.4; }
690
+ }
691
+
692
+ .๐Ÿ’€ {
693
+ background: var(--๐Ÿฉถ-light);
694
+ border-radius: var(--โญ•-md);
695
+ animation: ๐Ÿ’€-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
696
+ }
697
+
698
+ .๐Ÿ’€--๐Ÿ”  { height: 1.25em; width: 60%; }
699
+ .๐Ÿ’€--๐Ÿ“ { height: 1em; width: 100%; }
700
+ .๐Ÿ’€--๐Ÿ–ผ๏ธ { aspect-ratio: 16/9; width: 100%; }
701
+ .๐Ÿ’€--๐Ÿ‘ค { width: 40px; height: 40px; border-radius: 50%; }
702
+ .๐Ÿ’€--๐Ÿ”˜ { height: 36px; width: 80px; }
703
+
704
+ /* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
705
+ ๐Ÿ”„ Keyframes
706
+ โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
707
+
708
+ @keyframes ๐Ÿ”„-spin {
709
+ to { transform: rotate(360deg); }
710
+ }
711
+
712
+ @keyframes ๐Ÿ”„-fadeIn {
713
+ from { opacity: 0; transform: translateY(-8px); }
714
+ to { opacity: 1; transform: translateY(0); }
715
+ }
package/css/tokens.css ADDED
@@ -0,0 +1,110 @@
1
+ /**
2
+ * BEMoji Design Tokens
3
+ * All custom properties use emoji identifiers.
4
+ *
5
+ * Import this file in your project:
6
+ * @import 'bemoji/tokens';
7
+ */
8
+
9
+ :root {
10
+ /* โ”€โ”€ Colors โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */
11
+ --โฌ›: #0d0d0f;
12
+ --โฌœ: #ffffff;
13
+ --๐Ÿ”ด: #dc2626;
14
+ --๐Ÿ”ด-light: #fef2f2;
15
+ --๐Ÿ”ด-dark: #991b1b;
16
+ --๐ŸŸข: #16a34a;
17
+ --๐ŸŸข-light: #f0fdf4;
18
+ --๐ŸŸข-dark: #14532d;
19
+ --๐ŸŸก: #d97706;
20
+ --๐ŸŸก-light: #fffbeb;
21
+ --๐ŸŸก-dark: #92400e;
22
+ --๐Ÿ”ต: #2563eb;
23
+ --๐Ÿ”ต-light: #eff6ff;
24
+ --๐Ÿ”ต-dark: #1e3a8a;
25
+ --๐ŸŸฃ: #7c3aed;
26
+ --๐ŸŸฃ-light: #f5f3ff;
27
+ --๐ŸŸฃ-dark: #4c1d95;
28
+ --๐ŸŸ : #ea580c;
29
+ --โšซ: #111827;
30
+ --๐Ÿฉถ: #6b7280;
31
+ --๐Ÿฉถ-light: #f3f4f6;
32
+ --๐Ÿฉถ-dark: #374151;
33
+
34
+ /* โ”€โ”€ Spacing โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */
35
+ --๐Ÿ“-px: 1px;
36
+ --๐Ÿ“-0: 0;
37
+ --๐Ÿ“-0\\.5: 0.125rem; /* 2px */
38
+ --๐Ÿ“-1: 0.25rem; /* 4px */
39
+ --๐Ÿ“-1\\.5: 0.375rem; /* 6px */
40
+ --๐Ÿ“-2: 0.5rem; /* 8px */
41
+ --๐Ÿ“-2\\.5: 0.625rem; /* 10px */
42
+ --๐Ÿ“-3: 0.75rem; /* 12px */
43
+ --๐Ÿ“-4: 1rem; /* 16px */
44
+ --๐Ÿ“-5: 1.25rem; /* 20px */
45
+ --๐Ÿ“-6: 1.5rem; /* 24px */
46
+ --๐Ÿ“-7: 1.75rem; /* 28px */
47
+ --๐Ÿ“-8: 2rem; /* 32px */
48
+ --๐Ÿ“-10: 2.5rem; /* 40px */
49
+ --๐Ÿ“-12: 3rem; /* 48px */
50
+ --๐Ÿ“-14: 3.5rem; /* 56px */
51
+ --๐Ÿ“-16: 4rem; /* 64px */
52
+ --๐Ÿ“-20: 5rem; /* 80px */
53
+ --๐Ÿ“-24: 6rem; /* 96px */
54
+ --๐Ÿ“-32: 8rem; /* 128px */
55
+
56
+ /* โ”€โ”€ Typography โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */
57
+ --โœ๏ธ-xs: 0.75rem; /* 12px */
58
+ --โœ๏ธ-sm: 0.875rem; /* 14px */
59
+ --โœ๏ธ-base: 1rem; /* 16px */
60
+ --โœ๏ธ-lg: 1.125rem; /* 18px */
61
+ --โœ๏ธ-xl: 1.25rem; /* 20px */
62
+ --โœ๏ธ-2xl: 1.5rem; /* 24px */
63
+ --โœ๏ธ-3xl: 1.875rem; /* 30px */
64
+ --โœ๏ธ-4xl: 2.25rem; /* 36px */
65
+ --โœ๏ธ-5xl: 3rem; /* 48px */
66
+ --โœ๏ธ-6xl: 3.75rem; /* 60px */
67
+
68
+ --๐Ÿ“–-tight: 1.25;
69
+ --๐Ÿ“–-snug: 1.375;
70
+ --๐Ÿ“–-normal: 1.5;
71
+ --๐Ÿ“–-relaxed: 1.625;
72
+ --๐Ÿ“–-loose: 2;
73
+
74
+ /* โ”€โ”€ Shadows โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */
75
+ --๐ŸŒ‘-xs: 0 1px 2px rgba(0, 0, 0, 0.05);
76
+ --๐ŸŒ‘-sm: 0 1px 3px rgba(0, 0, 0, 0.10), 0 1px 2px rgba(0, 0, 0, 0.06);
77
+ --๐ŸŒ‘-md: 0 4px 6px rgba(0, 0, 0, 0.07), 0 2px 4px rgba(0, 0, 0, 0.06);
78
+ --๐ŸŒ‘-lg: 0 10px 15px rgba(0, 0, 0, 0.10), 0 4px 6px rgba(0, 0, 0, 0.05);
79
+ --๐ŸŒ‘-xl: 0 20px 25px rgba(0, 0, 0, 0.10), 0 10px 10px rgba(0, 0, 0, 0.04);
80
+ --๐ŸŒ‘-2xl: 0 25px 50px rgba(0, 0, 0, 0.25);
81
+ --๐ŸŒ‘-inner: inset 0 2px 4px rgba(0, 0, 0, 0.06);
82
+ --๐ŸŒ‘-none: none;
83
+
84
+ /* โ”€โ”€ Border Radius โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */
85
+ --โญ•-none: 0;
86
+ --โญ•-sm: 0.125rem; /* 2px */
87
+ --โญ•-md: 0.375rem; /* 6px */
88
+ --โญ•-lg: 0.5rem; /* 8px */
89
+ --โญ•-xl: 0.75rem; /* 12px */
90
+ --โญ•-2xl: 1rem; /* 16px */
91
+ --โญ•-3xl: 1.5rem; /* 24px */
92
+ --โญ•-full: 9999px;
93
+
94
+ /* โ”€โ”€ Transitions โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */
95
+ --โšก-fast: all 0.1s ease;
96
+ --โšก-base: all 0.15s ease;
97
+ --โšก-slow: all 0.3s ease;
98
+ --โšก-slower: all 0.5s ease;
99
+
100
+ /* โ”€โ”€ Z-index โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */
101
+ --๐Ÿ”๏ธ-0: 0;
102
+ --๐Ÿ”๏ธ-10: 10;
103
+ --๐Ÿ”๏ธ-20: 20;
104
+ --๐Ÿ”๏ธ-30: 30;
105
+ --๐Ÿ”๏ธ-40: 40;
106
+ --๐Ÿ”๏ธ-50: 50;
107
+ --๐Ÿ”๏ธ-modal: 1000;
108
+ --๐Ÿ”๏ธ-toast: 1100;
109
+ --๐Ÿ”๏ธ-tooltip: 1200;
110
+ }
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "bemoji",
3
+ "version": "1.0.0-beta.1",
4
+ "description": "BEMoji โ€” BEM methodology with emoji class names. Core framework.",
5
+ "main": "src/index.js",
6
+ "module": "src/index.js",
7
+ "exports": {
8
+ ".": "./src/index.js",
9
+ "./react": "./src/react.js",
10
+ "./base": "./css/base.css",
11
+ "./tokens": "./css/tokens.css",
12
+ "./components": "./css/components.css",
13
+ "./utilities": "./css/utilities.css"
14
+ },
15
+ "scripts": {
16
+ "test": "vitest run --passWithNoTests",
17
+ "test:watch": "vitest"
18
+ },
19
+ "keywords": ["css", "bem", "emoji", "framework", "design-system"],
20
+ "license": "MIT",
21
+ "files": [
22
+ "src/",
23
+ "css/"
24
+ ]
25
+ }
package/src/index.js ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * BEMoji โ€” Core Package
3
+ * @version 1.0.0-beta.1
4
+ */
5
+
6
+ export { encode, decode, escapeForCSS, toSelector, loadConfig } from './resolver.js';
7
+ export { BLOCKS, ELEMENTS, MODIFIERS, BREAKPOINTS, UTILITIES } from './vocabulary.js';
8
+ export { default as vocabulary } from './vocabulary.js';
package/src/react.js ADDED
@@ -0,0 +1,75 @@
1
+ /**
2
+ * BEMoji React Helper
3
+ *
4
+ * Provides the `bem()` utility function for resolving readable BEM names
5
+ * to emoji class names within React/JSX components.
6
+ *
7
+ * Usage:
8
+ * import { bem } from 'bemoji/react';
9
+ *
10
+ * <div className={bem('card')}> // โ†’ '๐Ÿƒ'
11
+ * <div className={bem('card__image')}> // โ†’ '๐Ÿƒ__๐Ÿ–ผ๏ธ'
12
+ * <div className={bem('card', { primary: true })}> // โ†’ '๐Ÿƒ ๐Ÿƒ--๐ŸŒŸ'
13
+ * <div className={bem('card__image', { featured })}> // โ†’ '๐Ÿƒ__๐Ÿ–ผ๏ธ ๐Ÿƒ__๐Ÿ–ผ๏ธ--๐ŸŒŸ'
14
+ */
15
+
16
+ import { encode, loadConfig } from './resolver.js';
17
+
18
+ let _config = loadConfig();
19
+
20
+ /**
21
+ * Configure the React helper with a custom BEMoji config.
22
+ * Call this once at your app's entry point.
23
+ *
24
+ * @param {object} userConfig
25
+ */
26
+ export function configure(userConfig) {
27
+ _config = loadConfig(userConfig);
28
+ }
29
+
30
+ /**
31
+ * Resolve a readable BEM name (with optional modifiers) to an emoji class string.
32
+ *
33
+ * @param {string} name Readable BEM name e.g. 'card', 'card__image', 'card__image--featured'
34
+ * @param {object} modifiers Object of modifier keys โ†’ boolean
35
+ * @param {string} extra Additional raw class names to append
36
+ * @returns {string} Space-separated emoji class string
37
+ */
38
+ export function bem(name, modifiers = {}, extra = '') {
39
+ const base = encode(name, _config);
40
+ const classes = [base];
41
+
42
+ for (const [key, active] of Object.entries(modifiers)) {
43
+ if (active) {
44
+ // Build the full modifier class: base + -- + modifier
45
+ const modClass = encode(`${name}--${key}`, _config);
46
+ classes.push(modClass);
47
+ }
48
+ }
49
+
50
+ if (extra) classes.push(extra);
51
+
52
+ return classes.filter(Boolean).join(' ');
53
+ }
54
+
55
+ /**
56
+ * Create a scoped `bem` function bound to a specific block.
57
+ * Useful for component files to avoid repeating the block name.
58
+ *
59
+ * @param {string} blockName e.g. 'card'
60
+ * @returns {Function} bem function scoped to that block
61
+ *
62
+ * @example
63
+ * const b = useBem('card');
64
+ * b() // โ†’ '๐Ÿƒ'
65
+ * b('image') // โ†’ '๐Ÿƒ__๐Ÿ–ผ๏ธ'
66
+ * b('image', { featured}) // โ†’ '๐Ÿƒ__๐Ÿ–ผ๏ธ ๐Ÿƒ__๐Ÿ–ผ๏ธ--๐ŸŒŸ'
67
+ */
68
+ export function useBem(blockName) {
69
+ return function(element, modifiers = {}, extra = '') {
70
+ const name = element ? `${blockName}__${element}` : blockName;
71
+ return bem(name, modifiers, extra);
72
+ };
73
+ }
74
+
75
+ export default bem;
@@ -0,0 +1,158 @@
1
+ /**
2
+ * BEMoji Resolver
3
+ *
4
+ * Resolves human-readable BEM strings to emoji class names,
5
+ * and provides utilities for encoding/decoding.
6
+ */
7
+
8
+ import { BLOCKS, ELEMENTS, MODIFIERS, BREAKPOINTS } from './vocabulary.js';
9
+
10
+ const SEP_ELEMENT = '__';
11
+ const SEP_MODIFIER = '--';
12
+
13
+ /**
14
+ * Load config, falling back to the default vocabulary.
15
+ * In real usage this would load from bemoji.config.js.
16
+ */
17
+ export function loadConfig(userConfig = {}) {
18
+ return {
19
+ blocks: { ...BLOCKS, ...userConfig.blocks },
20
+ elements: { ...ELEMENTS, ...userConfig.elements },
21
+ modifiers: { ...MODIFIERS, ...userConfig.modifiers },
22
+ breakpoints: { ...BREAKPOINTS, ...userConfig.breakpoints },
23
+ separator: {
24
+ element: SEP_ELEMENT,
25
+ modifier: SEP_MODIFIER,
26
+ ...userConfig.separator,
27
+ },
28
+ compiler: {
29
+ escape: 'auto',
30
+ sourceMap: true,
31
+ purge: true,
32
+ ...userConfig.compiler,
33
+ },
34
+ };
35
+ }
36
+
37
+ /**
38
+ * Encode a readable BEM string to emoji.
39
+ *
40
+ * @param {string} readableName e.g. 'card__image--featured'
41
+ * @param {object} config Loaded BEMoji config
42
+ * @returns {string} e.g. '๐Ÿƒ__๐Ÿ–ผ๏ธ--๐ŸŒŸ'
43
+ */
44
+ export function encode(readableName, config = loadConfig()) {
45
+ const { blocks, elements, modifiers, separator } = config;
46
+ const elSep = separator.element;
47
+ const modSep = separator.modifier;
48
+
49
+ let result = readableName;
50
+
51
+ // Split off modifiers first
52
+ const modSepIdx = result.indexOf(modSep);
53
+ let modifierPart = '';
54
+ if (modSepIdx !== -1) {
55
+ modifierPart = result.slice(modSepIdx + modSep.length);
56
+ result = result.slice(0, modSepIdx);
57
+ }
58
+
59
+ // Split block and element
60
+ const elSepIdx = result.indexOf(elSep);
61
+ let blockPart = result;
62
+ let elementPart = '';
63
+ if (elSepIdx !== -1) {
64
+ blockPart = result.slice(0, elSepIdx);
65
+ elementPart = result.slice(elSepIdx + elSep.length);
66
+ }
67
+
68
+ // Resolve each part
69
+ const blockEmoji = blocks[blockPart] || blockPart;
70
+ const elementEmoji = elements[elementPart] || elementPart;
71
+ const modifierEmoji = modifiers[modifierPart] || modifierPart;
72
+
73
+ // Reassemble
74
+ let encoded = blockEmoji;
75
+ if (elementPart) encoded += elSep + elementEmoji;
76
+ if (modifierPart) encoded += modSep + modifierEmoji;
77
+
78
+ return encoded;
79
+ }
80
+
81
+ /**
82
+ * Decode an emoji BEM class name back to readable form.
83
+ *
84
+ * @param {string} emojiClass e.g. '๐Ÿƒ__๐Ÿ–ผ๏ธ--๐ŸŒŸ'
85
+ * @param {object} config Loaded BEMoji config
86
+ * @returns {string} e.g. 'card__image--featured'
87
+ */
88
+ export function decode(emojiClass, config = loadConfig()) {
89
+ const { blocks, elements, modifiers, separator } = config;
90
+ const elSep = separator.element;
91
+ const modSep = separator.modifier;
92
+
93
+ // Build reverse maps
94
+ const rBlocks = Object.fromEntries(Object.entries(blocks).map(([k,v]) => [v,k]));
95
+ const rElements = Object.fromEntries(Object.entries(elements).map(([k,v]) => [v,k]));
96
+ const rModifiers = Object.fromEntries(Object.entries(modifiers).map(([k,v]) => [v,k]));
97
+
98
+ let result = emojiClass;
99
+
100
+ const modSepIdx = result.indexOf(modSep);
101
+ let modifierPart = '';
102
+ if (modSepIdx !== -1) {
103
+ modifierPart = result.slice(modSepIdx + modSep.length);
104
+ result = result.slice(0, modSepIdx);
105
+ }
106
+
107
+ const elSepIdx = result.indexOf(elSep);
108
+ let blockPart = result;
109
+ let elementPart = '';
110
+ if (elSepIdx !== -1) {
111
+ blockPart = result.slice(0, elSepIdx);
112
+ elementPart = result.slice(elSepIdx + elSep.length);
113
+ }
114
+
115
+ const blockName = rBlocks[blockPart] || blockPart;
116
+ const elementName = rElements[elementPart] || elementPart;
117
+ const modifierName = rModifiers[modifierPart] || modifierPart;
118
+
119
+ let decoded = blockName;
120
+ if (elementPart) decoded += elSep + elementName;
121
+ if (modifierPart) decoded += modSep + modifierName;
122
+
123
+ return decoded;
124
+ }
125
+
126
+ /**
127
+ * Escape an emoji string to CSS unicode escape sequences.
128
+ * Required for older browsers / certain CSS parsers.
129
+ *
130
+ * @param {string} emoji
131
+ * @returns {string} e.g. '\01F0CF'
132
+ */
133
+ export function escapeForCSS(emoji) {
134
+ return [...emoji]
135
+ .map(char => {
136
+ const cp = char.codePointAt(0);
137
+ if (cp > 0x7E) {
138
+ return '\\' + cp.toString(16).toUpperCase().padStart(6, '0');
139
+ }
140
+ return char;
141
+ })
142
+ .join('');
143
+ }
144
+
145
+ /**
146
+ * Generate a full CSS selector from a readable BEM name.
147
+ *
148
+ * @param {string} readableName
149
+ * @param {object} config
150
+ * @param {boolean} escaped Whether to output unicode escapes
151
+ * @returns {string} CSS selector string e.g. '.๐Ÿƒ__๐Ÿ–ผ๏ธ--๐ŸŒŸ'
152
+ */
153
+ export function toSelector(readableName, config = loadConfig(), escaped = false) {
154
+ const emoji = encode(readableName, config);
155
+ return escaped ? '.' + escapeForCSS(emoji) : '.' + emoji;
156
+ }
157
+
158
+ export default { encode, decode, escapeForCSS, toSelector, loadConfig };
@@ -0,0 +1,189 @@
1
+ /**
2
+ * BEMoji Canonical Vocabulary
3
+ * The official 143-token emoji lexicon.
4
+ *
5
+ * This file is the single source of truth for all reserved emoji tokens.
6
+ * Custom tokens are defined in bemoji.config.js and must not collide with these.
7
+ */
8
+
9
+ export const BLOCKS = {
10
+ card: '๐Ÿƒ',
11
+ navbar: '๐Ÿงญ',
12
+ footer: '๐Ÿฆถ',
13
+ form: '๐Ÿ“‹',
14
+ modal: '๐ŸชŸ',
15
+ alert: '๐Ÿ””',
16
+ badge: '๐Ÿท๏ธ',
17
+ tooltip: '๐Ÿ’ฌ',
18
+ table: '๐Ÿ“Š',
19
+ tabs: '๐Ÿ“‘',
20
+ carousel: '๐ŸŽ ',
21
+ breadcrumb: '๐Ÿž',
22
+ drawer: '๐Ÿ—‚๏ธ',
23
+ accordion: '๐Ÿ“‚',
24
+ avatar: '๐Ÿ‘ค',
25
+ progress: '๐Ÿ“ถ',
26
+ skeleton: '๐Ÿ’€',
27
+ pagination: '๐Ÿ“–',
28
+ dropdown: '๐Ÿ“ฅ',
29
+ stepper: '๐Ÿชœ',
30
+ timeline: '๐Ÿ“…',
31
+ sidebar: '๐Ÿ“Œ',
32
+ };
33
+
34
+ export const ELEMENTS = {
35
+ image: '๐Ÿ–ผ๏ธ',
36
+ title: '๐Ÿ” ',
37
+ body: '๐Ÿ“',
38
+ footer: '๐Ÿฆถ',
39
+ header: '๐Ÿช–',
40
+ button: '๐Ÿ”˜',
41
+ input: '๐Ÿ“ฅ',
42
+ link: '๐Ÿ”—',
43
+ label: '๐Ÿท๏ธ',
44
+ icon: '๐ŸŽญ',
45
+ item: '๐Ÿ“„',
46
+ divider: '๐Ÿ–‡๏ธ',
47
+ prefix: '๐Ÿ”ญ',
48
+ suffix: '๐Ÿ”ฌ',
49
+ overlay: '๐ŸŒซ๏ธ',
50
+ close: 'โœ–๏ธ',
51
+ badge: '๐Ÿ”ข',
52
+ avatar: '๐Ÿง‘',
53
+ count: '๐Ÿ”ข',
54
+ description: '๐Ÿ“–',
55
+ actions: 'โšก',
56
+ empty: '๐Ÿ•ณ๏ธ',
57
+ error: '๐Ÿšจ',
58
+ hint: '๐Ÿ’ก',
59
+ };
60
+
61
+ export const MODIFIERS = {
62
+ // Semantic states
63
+ primary: '๐ŸŒŸ',
64
+ secondary: '๐Ÿฅˆ',
65
+ danger: '๐Ÿ”ด',
66
+ success: '๐ŸŸข',
67
+ warning: '๐ŸŸก',
68
+ info: '๐Ÿ”ต',
69
+ neutral: 'โฌœ',
70
+
71
+ // Interactive states
72
+ disabled: '๐Ÿ‘ป',
73
+ active: 'โœ…',
74
+ loading: 'โณ',
75
+ locked: '๐Ÿ”’',
76
+ hidden: '๐Ÿ™ˆ',
77
+ visible: '๐Ÿ‘๏ธ',
78
+ selected: 'โ˜‘๏ธ',
79
+ focused: '๐Ÿ”',
80
+ hovered: '๐Ÿ‘†',
81
+ pressed: '๐Ÿ‘‡',
82
+ error: '๐Ÿšจ',
83
+ valid: 'โœ”๏ธ',
84
+ invalid: 'โŒ',
85
+
86
+ // Visual variants
87
+ dark: '๐Ÿ•ถ๏ธ',
88
+ light: 'โ˜€๏ธ',
89
+ ghost: '๐Ÿ‘๏ธ',
90
+ outline: 'โฌœ',
91
+ filled: 'โ– ',
92
+ flat: 'โ–ฌ',
93
+ elevated: 'โ˜๏ธ',
94
+ rounded: 'โญ•',
95
+ pill: '๐Ÿ’Š',
96
+ square: '๐ŸŸฅ',
97
+
98
+ // Content flags
99
+ premium: '๐Ÿ’Ž',
100
+ new: '๐Ÿ†•',
101
+ hot: '๐Ÿ”ฅ',
102
+ beta: '๐Ÿงช',
103
+ deprecated: 'โš ๏ธ',
104
+ required: 'โ—',
105
+ optional: 'โ“',
106
+
107
+ // Size modifiers
108
+ xs: '๐Ÿ”ฌ',
109
+ sm: '๐Ÿค',
110
+ md: 'โš–๏ธ',
111
+ lg: '๐Ÿ‹๏ธ',
112
+ xl: '๐Ÿ”๏ธ',
113
+ '2xl': '๐ŸŒ',
114
+ full: '๐ŸŸฆ',
115
+ };
116
+
117
+ export const BREAKPOINTS = {
118
+ sm: { prefix: '๐Ÿ“Ÿ', minWidth: '640px' },
119
+ md: { prefix: '๐Ÿ“ฒ', minWidth: '768px' },
120
+ lg: { prefix: '๐Ÿ’ป', minWidth: '1024px' },
121
+ xl: { prefix: '๐Ÿ–ฅ๏ธ', minWidth: '1280px' },
122
+ };
123
+
124
+ export const UTILITIES = {
125
+ // Display
126
+ block: '๐Ÿ“ฆ',
127
+ inline: 'โ–ถ๏ธ',
128
+ flex: '๐Ÿ”€',
129
+ grid: '๐Ÿ“',
130
+ hidden: '๐Ÿ™ˆ',
131
+ contents: '๐Ÿ“ƒ',
132
+
133
+ // Flex/Grid children
134
+ 'col-1': '๐Ÿ’ ',
135
+ 'col-2': '๐Ÿ”ฒ',
136
+ 'col-3': '๐Ÿ”ณ',
137
+ 'col-4': 'โฌ›',
138
+ 'col-auto': '๐Ÿ”„',
139
+ 'col-span-2': '๐Ÿ—‚๏ธ',
140
+ 'col-span-3': '๐Ÿ—“๏ธ',
141
+ 'col-span-full': '๐ŸŒ',
142
+
143
+ // Spacing (pattern: emoji + scale)
144
+ // p-{n}: ๐Ÿ“ prefix, m-{n}: ๐Ÿ“ prefix
145
+ // Gap
146
+ 'gap-1': 'โ†”๏ธ1',
147
+ 'gap-2': 'โ†”๏ธ2',
148
+ 'gap-4': 'โ†”๏ธ4',
149
+ 'gap-8': 'โ†”๏ธ8',
150
+
151
+ // Text align
152
+ 'text-left': 'โฌ…๏ธ',
153
+ 'text-center': 'โ†”๏ธ',
154
+ 'text-right': 'โžก๏ธ',
155
+
156
+ // Font weight
157
+ 'font-light': '๐Ÿชถ',
158
+ 'font-normal': 'โœ๏ธ',
159
+ 'font-bold': '๐Ÿ–Š๏ธ',
160
+ 'font-black': '๐Ÿ–‹๏ธ',
161
+
162
+ // Overflow
163
+ 'overflow-hidden': 'โœ‚๏ธ',
164
+ 'overflow-auto': 'โ†•๏ธ',
165
+ 'overflow-scroll': '๐Ÿ“œ',
166
+
167
+ // Position
168
+ relative: '๐Ÿ”',
169
+ absolute: '๐Ÿ“',
170
+ fixed: '๐Ÿ“Œ',
171
+ sticky: '๐Ÿฉน',
172
+
173
+ // Width / Height
174
+ 'w-full': '๐ŸŒŠ',
175
+ 'h-full': '๐Ÿ—๏ธ',
176
+ 'w-screen': '๐Ÿ–ฅ๏ธ',
177
+ 'h-screen': '๐Ÿ“บ',
178
+
179
+ // Visibility
180
+ invisible: '๐Ÿ‘ป',
181
+ 'sr-only': '๐Ÿ”‡',
182
+ };
183
+
184
+ // Build reverse lookup maps (emoji โ†’ name)
185
+ export const REVERSE_BLOCKS = Object.fromEntries(Object.entries(BLOCKS).map(([k,v]) => [v,k]));
186
+ export const REVERSE_ELEMENTS = Object.fromEntries(Object.entries(ELEMENTS).map(([k,v]) => [v,k]));
187
+ export const REVERSE_MODIFIERS = Object.fromEntries(Object.entries(MODIFIERS).map(([k,v]) => [v,k]));
188
+
189
+ export default { BLOCKS, ELEMENTS, MODIFIERS, BREAKPOINTS, UTILITIES };