twntyx-css 1.0.10 → 1.0.12

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/README.md CHANGED
@@ -28,12 +28,47 @@ npm install twntyx-css
28
28
 
29
29
  ### 3. Import the css
30
30
 
31
- Import the Framework CSS into your project stylesheet.
31
+ Import the framework stylesheets into your project. Load fonts before globals so bundlers resolve packaged font files correctly:
32
32
 
33
33
  ```css
34
+ @import url('twntyx-css/styles/font.css');
34
35
  @import 'twntyx-css/styles/globals.css';
35
36
  ```
36
37
 
38
+ Or import both in your app entry:
39
+
40
+ ```js
41
+ import 'twntyx-css/styles/font.css';
42
+ import 'twntyx-css/styles/globals.css';
43
+ ```
44
+
45
+ ### Nuxt + Tailwind CSS v4
46
+
47
+ ```ts
48
+ // nuxt.config.ts
49
+ export default defineNuxtConfig({
50
+ css: [
51
+ 'twntyx-css/styles/font.css',
52
+ 'twntyx-css/styles/globals.css',
53
+ ],
54
+ postcss: {
55
+ plugins: {
56
+ '@tailwindcss/postcss': {},
57
+ },
58
+ },
59
+ })
60
+ ```
61
+
62
+ twntyx-css ships `@font-face` rules for Instrument Sans and Gazpacho, so `@nuxt/fonts` is optional.
63
+
64
+ ### Fonts troubleshooting
65
+
66
+ If fonts fall back to system defaults:
67
+
68
+ 1. Import `twntyx-css/styles/font.css` before `twntyx-css/styles/globals.css`.
69
+ 2. In DevTools Network, confirm font requests resolve to bundled assets (not `/fonts/...` at your site root).
70
+ 3. After upgrading, clear `.nuxt/` and `node_modules/.cache`.
71
+
37
72
  ### 4. Adapt webpack config.
38
73
 
39
74
  Add postcss-loader to webpack.config.js.
@@ -17,64 +17,19 @@
17
17
  "new message state"
18
18
  ],
19
19
  "sourcePaths": [
20
- "app/design-system/chat-ai/page.js",
21
- "assets/styles/button.css",
22
- "assets/styles/chat.css",
23
- "assets/styles/form-text-select.css",
24
- "assets/styles/panel.css",
25
- "assets/styles/toast.css"
20
+ "app/design-system/chat-ai/page.js"
26
21
  ],
27
22
  "apiModel": "html-class",
28
23
  "baseClass": "chat-ai",
29
- "variants": [
30
- {
31
- "name": "chat-secondary",
32
- "className": "chat-secondary",
33
- "description": "AI chat visual variant styling."
34
- }
35
- ],
36
- "sizes": [
37
- {
38
- "name": "chat-loop-stack-lg",
39
- "className": "chat-loop-stack-lg",
40
- "description": "Large min-height variant for loop stack."
41
- }
42
- ],
43
- "states": [
44
- {
45
- "name": "chat-loop-loading",
46
- "className": "chat-loop-loading",
47
- "description": "Animated loading row in loop simulation."
48
- },
49
- {
50
- "name": "chat-loop-message",
51
- "className": "chat-loop-message",
52
- "description": "Animated resolved message row in loop simulation."
53
- },
54
- {
55
- "name": "is-owner",
56
- "className": "is-owner",
57
- "description": "Marks messages authored by the current user."
58
- },
59
- {
60
- "name": "is-loading",
61
- "className": "is-loading",
62
- "description": "Shows loading placeholder behavior for a message group."
63
- }
64
- ],
24
+ "variants": [],
25
+ "sizes": [],
26
+ "states": [],
65
27
  "structure": {
66
28
  "rootElement": "div",
67
29
  "requiredClasses": [
68
30
  "chat-ai"
69
31
  ],
70
- "optionalClasses": [
71
- "chat-secondary",
72
- "chat-loop-stack-lg",
73
- "chat-loop-loading",
74
- "chat-loop-message",
75
- "is-owner",
76
- "is-loading"
77
- ],
32
+ "optionalClasses": [],
78
33
  "requiredChildren": [],
79
34
  "optionalChildren": []
80
35
  },
@@ -114,56 +69,7 @@
114
69
  "Do not rely on undocumented internal classes in production templates.",
115
70
  "Do not remove required accessibility attributes when component is interactive."
116
71
  ],
117
- "tokenUsage": [
118
- "background-brand",
119
- "background-brand-active",
120
- "background-error",
121
- "background-error-active",
122
- "background-info",
123
- "background-info-active",
124
- "background-inverted",
125
- "background-page",
126
- "background-success",
127
- "background-success-active",
128
- "background-surface",
129
- "background-surface-elevated",
130
- "background-surface-sunken",
131
- "background-warning",
132
- "background-warning-active",
133
- "brand-default",
134
- "brand-moderate",
135
- "brand-strong",
136
- "core-black",
137
- "core-ui-100",
138
- "core-ui-50",
139
- "core-ui-700",
140
- "core-ui-800",
141
- "core-ui-900",
142
- "core-white",
143
- "line-default",
144
- "line-error",
145
- "line-focus",
146
- "line-highlight",
147
- "line-info",
148
- "line-strong",
149
- "line-success",
150
- "line-warning",
151
- "text-attention",
152
- "text-brand",
153
- "text-brand-active",
154
- "text-brand-hover",
155
- "text-default",
156
- "text-disabled",
157
- "text-error",
158
- "text-info",
159
- "text-inverted",
160
- "text-link",
161
- "text-link-hover",
162
- "text-secondary",
163
- "text-success",
164
- "text-tertiary",
165
- "text-warning"
166
- ],
72
+ "tokenUsage": [],
167
73
  "examples": [
168
74
  {
169
75
  "id": "canonical",
@@ -66,6 +66,11 @@
66
66
  "name": "menu-rounded-full",
67
67
  "className": "menu-rounded-full",
68
68
  "description": "Applies fully rounded corners to menu items"
69
+ },
70
+ {
71
+ "name": "menu-row-action-hover",
72
+ "className": "menu-row-action-hover",
73
+ "description": "Reveals the row action button only on row hover or focus"
69
74
  }
70
75
  ],
71
76
  "sizes": [
@@ -116,6 +121,7 @@
116
121
  "disabled",
117
122
  "menu-rounded-none",
118
123
  "menu-rounded-full",
124
+ "menu-row-action-hover",
119
125
  "menu-xs",
120
126
  "menu-sm",
121
127
  "menu-lg",
@@ -1 +1 @@
1
- <div class="chat-ai chat-secondary">AI chat structure</div>
1
+ <div class="chat-ai">AI chat structure</div>
package/llm/index.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "llmSchemaVersion": "1.0.0",
3
- "designSystemVersion": "1.0.10",
3
+ "designSystemVersion": "1.0.12",
4
4
  "packageName": "twntyx-css",
5
5
  "apiModel": "html-class",
6
- "sourceFingerprint": "f7c8ee0540e7bb413846b1be281b9f5034f86ab48ade5f990858bbcce6fea5ad",
6
+ "sourceFingerprint": "b01e7500be1e207fadfd0231734490a316c01e8aa0452755ec72ddedf3ce711d",
7
7
  "files": {
8
8
  "schema": "schema.json",
9
9
  "tokens": "tokens.json",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "twntyx-css",
3
- "version": "1.0.10",
3
+ "version": "1.0.12",
4
4
  "exports": {
5
5
  "./llm": "./llm/index.json",
6
6
  "./llm/components/*": "./llm/components/*.json",
@@ -11,6 +11,7 @@
11
11
  "./*": "./*",
12
12
  "./fonts/*": "./fonts/*",
13
13
  "./styles": "./styles/globals.css",
14
+ "./styles/font.css": "./styles/font.css",
14
15
  "./styles/*": "./styles/*",
15
16
  "./tailwind.config": "./tailwind.config.cjs"
16
17
  },
package/styles/button.css CHANGED
@@ -310,7 +310,7 @@
310
310
  }
311
311
 
312
312
  @utility button-destructive {
313
- @apply border backdrop-blur include-border-destructive bg-background-error border-line-error hover:border-line-error text-text-error active:bg-background-surface-elevated;
313
+ @apply border backdrop-blur include-border-destructive bg-background-error border-line-error hover:border-text-error text-text-error active:bg-background-surface-elevated;
314
314
 
315
315
  &.is-active {
316
316
  @apply bg-background-surface-elevated;
@@ -318,7 +318,7 @@
318
318
  }
319
319
 
320
320
  @utility button-constructive {
321
- @apply border backdrop-blur include-border-destructive bg-background-success border-line-success hover:border-line-success text-text-success active:bg-background-surface-elevated;
321
+ @apply border backdrop-blur include-border-destructive bg-background-success border-line-success hover:border-text-success text-text-success active:bg-background-surface-elevated;
322
322
 
323
323
  &.is-active {
324
324
  @apply bg-background-surface-elevated;
package/styles/chat.css CHANGED
@@ -332,7 +332,9 @@
332
332
  top-0
333
333
  md:top-3.5
334
334
  left-0
335
- animate-loading-text-gradient;
335
+ animate-loading-text-gradient
336
+ from-text-default
337
+ to-text-default/10;
336
338
  }
337
339
  }
338
340
  }
package/styles/font.css CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  @font-face {
6
6
  font-family: "Instrument Sans";
7
- src: url("../fonts/InstrumentSans-VariableFont_wdth,wght.ttf") format("truetype");
7
+ src: url("../fonts/InstrumentSans-Variable.ttf") format("truetype");
8
8
  font-weight: 100 900;
9
9
  font-style: normal;
10
10
  font-display: swap;
@@ -12,7 +12,7 @@
12
12
 
13
13
  @font-face {
14
14
  font-family: "Instrument Sans";
15
- src: url("../fonts/InstrumentSans-Italic-VariableFont_wdth,wght.ttf") format("truetype");
15
+ src: url("../fonts/InstrumentSans-Italic-Variable.ttf") format("truetype");
16
16
  font-weight: 100 900;
17
17
  font-style: italic;
18
18
  font-display: swap;
@@ -1,3 +1,4 @@
1
+ @import url("./font.css");
1
2
  @import 'tailwindcss';
2
3
  @import './theme.css';
3
4
 
@@ -25,7 +26,6 @@
25
26
  @import './drawer.css';
26
27
  @import './fieldset.css';
27
28
  @import './field.css';
28
- @import './font.css';
29
29
  @import './dropdown.css';
30
30
  @import './form-shared.css';
31
31
  @import './form-checkbox-radio-toggle.css';
package/styles/menu.css CHANGED
@@ -21,7 +21,7 @@
21
21
  @apply flex relative flex-col flex-wrap items-stretch shrink-0;
22
22
 
23
23
  /* Default Link/Button Style */
24
- > *:not(ul, details, .menu-title) {
24
+ > *:not(ul, details, .menu-title, .menu-row) {
25
25
  @apply flex gap-2 items-center px-4 py-2 w-full max-w-full text-sm rounded cursor-pointer include-hover-primary include-interactive text-text-tertiary hover:text-text-default disabled:cursor-not-allowed disabled:text-text-disabled disabled:hover:bg-transparent;
26
26
 
27
27
  &:active {
@@ -31,6 +31,44 @@
31
31
  }
32
32
  }
33
33
 
34
+ /* Split row: main link + contextual action */
35
+ > .menu-row {
36
+ @apply flex gap-1 items-center px-4 py-2 w-full max-w-full text-sm rounded cursor-default include-hover-primary include-interactive text-text-tertiary hover:text-text-default;
37
+
38
+ > :where(a, button:not(.menu-row-action)) {
39
+ @apply flex flex-1 gap-2 items-center min-w-0 p-0 rounded-none cursor-pointer text-inherit hover:text-inherit outline-transparent focus-visible:outline-transparent active:outline-transparent;
40
+
41
+ &::before {
42
+ @apply hidden;
43
+ }
44
+
45
+ &:active > * {
46
+ @apply translate-y-px duration-0;
47
+ }
48
+ }
49
+
50
+ > .menu-row-action {
51
+ @apply relative z-10 flex shrink-0 justify-center items-center size-6 rounded cursor-pointer include-interactive text-text-tertiary hover:text-text-default;
52
+
53
+ &::before {
54
+ @apply hidden;
55
+ }
56
+
57
+ svg {
58
+ @apply fill-current size-4;
59
+ }
60
+ }
61
+ }
62
+
63
+ &.menu-row-action-hover > .menu-row > .menu-row-action {
64
+ @apply opacity-0 pointer-events-none transition-opacity duration-75;
65
+ }
66
+
67
+ &.menu-row-action-hover > .menu-row:hover > .menu-row-action,
68
+ &.menu-row-action-hover > .menu-row:focus-within > .menu-row-action {
69
+ @apply opacity-100 pointer-events-auto;
70
+ }
71
+
34
72
  span {
35
73
  @apply truncate min-w-fit;
36
74
  }
@@ -39,7 +77,7 @@
39
77
  &:has(svg:only-child) {
40
78
  @apply px-2;
41
79
  }
42
- > *:not(svg, .avatar) {
80
+ > *:not(svg, .avatar, .material-symbols-rounded) {
43
81
  @apply w-full;
44
82
  }
45
83
  svg {
@@ -51,8 +89,12 @@
51
89
  @apply include-interactive;
52
90
  }
53
91
 
92
+ > .menu-row > :where(a, button:not(.menu-row-action)) {
93
+ @apply outline-transparent focus-visible:outline-transparent active:outline-transparent;
94
+ }
95
+
54
96
  &.is-selected {
55
- > *:not(ul, details, .menu-title) {
97
+ > *:not(ul, details, .menu-title, .menu-row) {
56
98
  @apply include-hover-primary-hover;
57
99
  }
58
100
  }
@@ -95,11 +137,23 @@
95
137
  }
96
138
  }
97
139
 
140
+ &.is-disabled > .menu-row {
141
+ @apply cursor-not-allowed text-text-disabled hover:text-text-disabled;
142
+
143
+ > :where(a, button) {
144
+ @apply pointer-events-none;
145
+ }
146
+ }
147
+
98
148
  &.is-active > * {
99
149
  @apply font-medium include-hover-primary-active text-text-brand-active hover:text-text-brand-active;
100
150
  }
101
151
 
102
- &:has(:focus-visible) > * {
152
+ &.is-active > .menu-row {
153
+ @apply font-medium include-hover-primary-active text-text-brand-active hover:text-text-brand-active;
154
+ }
155
+
156
+ &:has(:focus-visible) > *:not(.menu-row) {
103
157
  @apply outline-line-brand;
104
158
  }
105
159
  }
@@ -157,8 +211,11 @@
157
211
  @utility menu-xs {
158
212
  @apply text-xs;
159
213
  li {
214
+ > .menu-row {
215
+ @apply gap-1 px-2 py-1 text-xs;
216
+ }
160
217
  > * {
161
- &:not(ul, details, .menu-title),
218
+ &:not(ul, details, .menu-title, .menu-row),
162
219
  details > summary {
163
220
  @apply gap-1 px-2 py-1 text-xs;
164
221
  }
@@ -183,8 +240,11 @@
183
240
  @utility menu-sm {
184
241
  @apply text-xs;
185
242
  li {
243
+ > .menu-row {
244
+ @apply gap-1.5 px-3 py-1.5 text-xs;
245
+ }
186
246
  > * {
187
- &:not(ul, details, .menu-title),
247
+ &:not(ul, details, .menu-title, .menu-row),
188
248
  details > summary {
189
249
  @apply gap-1.5 px-3 py-1.5 text-xs;
190
250
  }
@@ -207,12 +267,15 @@
207
267
  }
208
268
 
209
269
  @utility menu-lg {
210
- @apply text-xs;
270
+ @apply text-base;
211
271
  li {
272
+ > .menu-row {
273
+ @apply gap-3 px-5 py-3 text-base;
274
+ }
212
275
  > * {
213
- &:not(ul, details, .menu-title),
276
+ &:not(ul, details, .menu-title, .menu-row),
214
277
  details > summary {
215
- @apply gap-3 px-5 py-3 text-xs;
278
+ @apply gap-3 px-5 py-3 text-base;
216
279
  }
217
280
  &:has(svg:only-child) {
218
281
  @apply px-3;
@@ -235,7 +298,8 @@
235
298
  /* Roundness modifiers */
236
299
  @utility menu-rounded-none {
237
300
  li {
238
- > *:not(ul, details, .menu-title),
301
+ > *:not(ul, details, .menu-title, .menu-row),
302
+ > .menu-row,
239
303
  details > summary {
240
304
  &,
241
305
  &::before {
@@ -247,7 +311,8 @@
247
311
 
248
312
  @utility menu-rounded-sm {
249
313
  li {
250
- > *:not(ul, details, .menu-title),
314
+ > *:not(ul, details, .menu-title, .menu-row),
315
+ > .menu-row,
251
316
  details > summary {
252
317
  &,
253
318
  &::before {
@@ -259,7 +324,8 @@
259
324
 
260
325
  @utility menu-rounded-lg {
261
326
  li {
262
- > *:not(ul, details, .menu-title),
327
+ > *:not(ul, details, .menu-title, .menu-row),
328
+ > .menu-row,
263
329
  details > summary {
264
330
  &,
265
331
  &::before {
@@ -271,7 +337,8 @@
271
337
 
272
338
  @utility menu-rounded-full {
273
339
  li {
274
- > *:not(ul, details, .menu-title),
340
+ > *:not(ul, details, .menu-title, .menu-row),
341
+ > .menu-row,
275
342
  details > summary {
276
343
  &,
277
344
  &::before {
@@ -110,3 +110,7 @@ h6 {
110
110
  }
111
111
  }
112
112
  }
113
+
114
+ .material-symbols-rounded {
115
+ @apply leading-none;
116
+ }