@rokkit/themes 1.0.0-next.127 → 1.0.0-next.129

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/build.mjs ADDED
@@ -0,0 +1,237 @@
1
+ /**
2
+ * @rokkit/themes — CSS Build Script
3
+ *
4
+ * Compiles theme CSS with UnoCSS, expanding @apply directives into real CSS
5
+ * (including correct dark mode sibling rules).
6
+ *
7
+ * Outputs separate files per theme:
8
+ * dist/base.css — structural styles + default CSS variable palette
9
+ * dist/rokkit.css — rokkit theme (gradients, glowing borders)
10
+ * dist/minimal.css — minimal theme (clean, subtle)
11
+ * dist/material.css — material theme (elevation, shadows)
12
+ * dist/glass.css — glass theme (blur, transparency)
13
+ *
14
+ * Post-processing: adds compound selector form so dark mode works when
15
+ * data-mode and data-style are on the SAME element (e.g. body):
16
+ * Generated: [data-mode="dark"] [data-style="rokkit"] [data-toggle]
17
+ * Added also: [data-mode="dark"][data-style="rokkit"] [data-toggle]
18
+ */
19
+
20
+ import { createGenerator, presetWind3, transformerDirectives } from 'unocss'
21
+ import { Theme } from '@rokkit/core'
22
+ import { readFileSync, writeFileSync, mkdirSync } from 'fs'
23
+ import { resolve, dirname, join } from 'path'
24
+ import { fileURLToPath } from 'url'
25
+ import MagicString from 'magic-string'
26
+
27
+ const __dirname = dirname(fileURLToPath(import.meta.url))
28
+
29
+ // ─── UnoCSS config ────────────────────────────────────────────────────────────
30
+
31
+ const theme = new Theme()
32
+
33
+ const uno = await createGenerator({
34
+ presets: [
35
+ presetWind3({
36
+ dark: {
37
+ light: '[data-mode="light"]',
38
+ dark: '[data-mode="dark"]'
39
+ }
40
+ })
41
+ ],
42
+ shortcuts: [
43
+ ['skin-default', theme.getPalette()],
44
+ ...theme.getShortcuts('surface'),
45
+ ...theme.getShortcuts('primary'),
46
+ ...theme.getShortcuts('secondary'),
47
+ ...theme.getShortcuts('accent'),
48
+ ...theme.getShortcuts('success'),
49
+ ...theme.getShortcuts('warning'),
50
+ ...theme.getShortcuts('danger'),
51
+ ...theme.getShortcuts('error'),
52
+ ...theme.getShortcuts('info'),
53
+ ['text-on-primary', 'text-surface-50'],
54
+ ['text-on-secondary', 'text-surface-50'],
55
+ ['text-on-info', 'text-surface-50'],
56
+ ['text-on-success', 'text-surface-50'],
57
+ ['text-on-warning', 'text-surface-50'],
58
+ ['text-on-error', 'text-surface-50'],
59
+ ['text-on-surface', 'text-surface-50']
60
+ ],
61
+ theme: {
62
+ colors: theme.getColorRules()
63
+ }
64
+ })
65
+
66
+ // ─── CSS @import resolver ─────────────────────────────────────────────────────
67
+
68
+ /**
69
+ * Recursively inlines local @import statements.
70
+ * External imports (http, @, ~) are left unchanged.
71
+ */
72
+ function resolveImports(filePath, seen = new Set()) {
73
+ if (seen.has(filePath)) return ''
74
+ seen.add(filePath)
75
+
76
+ const content = readFileSync(filePath, 'utf-8')
77
+ return content.replace(/@import\s+['"]([^'"]+)['"]\s*;/g, (match, importPath) => {
78
+ if (importPath.startsWith('http') || importPath.startsWith('@') || importPath.startsWith('~')) {
79
+ return match
80
+ }
81
+ const resolvedPath = resolve(dirname(filePath), importPath)
82
+ return resolveImports(resolvedPath, seen)
83
+ })
84
+ }
85
+
86
+ // ─── UnoCSS @apply processor ─────────────────────────────────────────────────
87
+
88
+ const transformer = transformerDirectives()
89
+
90
+ async function processCSS(content, filename) {
91
+ const s = new MagicString(content)
92
+ await transformer.transform(s, filename, {
93
+ uno,
94
+ tokens: new Set(),
95
+ generate: async (tokens) => uno.generate(tokens, { preflights: false })
96
+ })
97
+ return s.toString()
98
+ }
99
+
100
+ // ─── Post-processing: fix dark mode selectors ─────────────────────────────────
101
+
102
+ /**
103
+ * UnoCSS generates dark mode rules as descendant selectors:
104
+ * [data-mode="dark"] [data-style="rokkit"] [data-component]
105
+ *
106
+ * This doesn't match when both data-mode and data-style are on the SAME element
107
+ * (e.g. <body data-mode="dark" data-style="rokkit">).
108
+ *
109
+ * For each such rule, we also add the compound selector form:
110
+ * [data-mode="dark"][data-style="rokkit"] [data-component]
111
+ *
112
+ * Both forms are emitted so either usage pattern works.
113
+ */
114
+ function fixModeSelectors(css) {
115
+ const modePattern = /\[data-mode="(?:dark|light)"\] \[data-style="[^"]+"\]/
116
+
117
+ let result = ''
118
+ let i = 0
119
+
120
+ while (i < css.length) {
121
+ // Find next { (opening of a declarations block)
122
+ const braceOpen = css.indexOf('{', i)
123
+ if (braceOpen === -1) {
124
+ result += css.slice(i)
125
+ break
126
+ }
127
+
128
+ const selectorText = css.slice(i, braceOpen)
129
+
130
+ // Only expand selectors that contain the problematic pattern
131
+ if (modePattern.test(selectorText)) {
132
+ // Split on comma, but only top-level commas (not inside :not(), :is(), etc.)
133
+ const parts = splitTopLevelSelectors(selectorText)
134
+ const expanded = []
135
+
136
+ for (const part of parts) {
137
+ const m = part.match(/^(\s*)(\[data-mode="(?:dark|light)"\]) (\[data-style="[^"]+"\])(.*)$/)
138
+ if (m) {
139
+ const [, ws, modeSelector, styleSelector, rest] = m
140
+ // Compound form: [data-mode="X"][data-style="Y"]rest (same-element match)
141
+ expanded.push(`${ws}${modeSelector}${styleSelector}${rest}`)
142
+ }
143
+ // Always include original descendant form
144
+ expanded.push(part)
145
+ }
146
+
147
+ result += expanded.join(',') + '{'
148
+ } else {
149
+ result += selectorText + '{'
150
+ }
151
+
152
+ // Find the matching closing brace, handling nesting
153
+ let depth = 1
154
+ let j = braceOpen + 1
155
+ while (j < css.length && depth > 0) {
156
+ if (css[j] === '{') depth++
157
+ else if (css[j] === '}') depth--
158
+ j++
159
+ }
160
+
161
+ result += css.slice(braceOpen + 1, j)
162
+ i = j
163
+ }
164
+
165
+ return result
166
+ }
167
+
168
+ /**
169
+ * Split a selector list by top-level commas only (not inside parentheses).
170
+ */
171
+ function splitTopLevelSelectors(text) {
172
+ const parts = []
173
+ let depth = 0
174
+ let start = 0
175
+
176
+ for (let i = 0; i < text.length; i++) {
177
+ if (text[i] === '(') depth++
178
+ else if (text[i] === ')') depth--
179
+ else if (text[i] === ',' && depth === 0) {
180
+ parts.push(text.slice(start, i))
181
+ start = i + 1
182
+ }
183
+ }
184
+ parts.push(text.slice(start))
185
+ return parts
186
+ }
187
+
188
+ // ─── Build ────────────────────────────────────────────────────────────────────
189
+
190
+ const srcDir = join(__dirname, 'src')
191
+ const distDir = join(__dirname, 'dist')
192
+
193
+ async function buildFile(inputPath, outputName, label) {
194
+ const fullCSS = resolveImports(inputPath)
195
+ const compiled = await processCSS(fullCSS, outputName)
196
+ const fixed = fixModeSelectors(compiled)
197
+ writeFileSync(join(distDir, outputName), fixed, 'utf-8')
198
+ console.log(`✓ dist/${outputName} (${label})`)
199
+ }
200
+
201
+ async function build() {
202
+ mkdirSync(distDir, { recursive: true })
203
+
204
+ // base.css: structural styles + palette CSS variable defaults
205
+ const paletteCSS = readFileSync(join(srcDir, 'palette.css'), 'utf-8')
206
+ const compiledPalette = await processCSS(paletteCSS, 'palette.css')
207
+ const baseCSS = resolveImports(join(srcDir, 'base', 'index.css'))
208
+ const compiledBase = await processCSS(baseCSS, 'base.css')
209
+ const baseFull = fixModeSelectors(compiledPalette + '\n' + compiledBase)
210
+ writeFileSync(join(distDir, 'base.css'), baseFull, 'utf-8')
211
+ console.log('✓ dist/base.css (structural styles + palette defaults)')
212
+
213
+ // Per-theme files
214
+ for (const [name, label] of [
215
+ ['rokkit', 'gradients + glowing borders'],
216
+ ['minimal', 'clean + subtle'],
217
+ ['material', 'elevation + shadows'],
218
+ ['glass', 'blur + transparency']
219
+ ]) {
220
+ await buildFile(join(srcDir, name, 'index.css'), `${name}.css`, label)
221
+ }
222
+
223
+ // Full bundle: base + all themes
224
+ const allThemes = ['base', 'rokkit', 'minimal', 'material', 'glass']
225
+ const bundleParts = allThemes.map((name) =>
226
+ readFileSync(join(distDir, `${name}.css`), 'utf-8')
227
+ )
228
+ writeFileSync(join(distDir, 'index.css'), bundleParts.join('\n'), 'utf-8')
229
+ console.log('✓ dist/index.css (full bundle)')
230
+
231
+ console.log('\n@rokkit/themes build complete.')
232
+ }
233
+
234
+ build().catch((err) => {
235
+ console.error('Build failed:', err)
236
+ process.exit(1)
237
+ })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rokkit/themes",
3
- "version": "1.0.0-next.127",
3
+ "version": "1.0.0-next.129",
4
4
  "description": "Theme styles for @rokkit/ui components",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -11,6 +11,12 @@
11
11
  "default": "./src/index.js"
12
12
  },
13
13
  "./styles": "./src/index.css",
14
+ "./dist": "./dist/index.css",
15
+ "./base.css": "./dist/base.css",
16
+ "./rokkit.css": "./dist/rokkit.css",
17
+ "./minimal.css": "./dist/minimal.css",
18
+ "./material.css": "./dist/material.css",
19
+ "./glass.css": "./dist/glass.css",
14
20
  "./base": "./src/base/index.css",
15
21
  "./base/*": "./src/base/*.css",
16
22
  "./rokkit": "./src/rokkit/index.css",
@@ -20,17 +26,22 @@
20
26
  "./material": "./src/material/index.css",
21
27
  "./material/*": "./src/material/*.css",
22
28
  "./glass": "./src/glass/index.css",
23
- "./glass/*": "./src/glass/*.css",
24
- "./palette.css": "./src/palette.css"
29
+ "./glass/*": "./src/glass/*.css"
25
30
  },
26
31
  "files": [
27
- "src"
32
+ "src",
33
+ "dist",
34
+ "build.mjs"
28
35
  ],
29
36
  "scripts": {
30
- "build": "echo 'No build step needed for CSS-only package'"
37
+ "build": "bun run build.mjs"
31
38
  },
32
39
  "dependencies": {
33
- "@rokkit/core": "1.0.0-next.127"
40
+ "@rokkit/core": "1.0.0-next.129"
41
+ },
42
+ "devDependencies": {
43
+ "magic-string": "^0.30.21",
44
+ "unocss": "^66.5.1"
34
45
  },
35
46
  "keywords": [
36
47
  "themes",
@@ -35,6 +35,7 @@
35
35
  border-radius: 0.25rem;
36
36
  font: inherit;
37
37
  color: inherit;
38
+ text-decoration: none;
38
39
  }
39
40
 
40
41
  [data-breadcrumb-current] {
@@ -57,6 +57,10 @@
57
57
  padding: 0;
58
58
  }
59
59
 
60
+ [data-button][data-icon-only] [data-item-text] {
61
+ display: none;
62
+ }
63
+
60
64
  /* =============================================================================
61
65
  Size Variants
62
66
  ============================================================================= */
@@ -75,7 +79,7 @@
75
79
 
76
80
  [data-button][data-size='sm'] [data-item-icon],
77
81
  [data-button][data-size='sm'] [data-button-icon-right] {
78
- font-size: 0.875rem;
82
+ font-size: 1.25rem;
79
83
  }
80
84
 
81
85
  /* Medium (default) */
@@ -96,7 +100,7 @@
96
100
  [data-button][data-size='md'] [data-button-icon-right],
97
101
  [data-button]:not([data-size]) [data-item-icon],
98
102
  [data-button]:not([data-size]) [data-button-icon-right] {
99
- font-size: 1rem;
103
+ font-size: 1.5rem;
100
104
  }
101
105
 
102
106
  /* Large */
@@ -113,7 +117,7 @@
113
117
 
114
118
  [data-button][data-size='lg'] [data-item-icon],
115
119
  [data-button][data-size='lg'] [data-button-icon-right] {
116
- font-size: 1.25rem;
120
+ font-size: 1.875rem;
117
121
  }
118
122
 
119
123
  /* =============================================================================
@@ -48,8 +48,6 @@
48
48
  [data-connector-v] {
49
49
  grid-column: 1;
50
50
  grid-row: 1;
51
- border-right-width: 1px;
52
- border-right-style: solid;
53
51
  }
54
52
 
55
53
  /* Vertical line - full height (for 'child' and 'sibling' connectors) */
@@ -61,18 +59,12 @@
61
59
  [data-connector-h] {
62
60
  grid-column: 2;
63
61
  grid-row: 1;
64
- border-bottom-width: 1px;
65
- border-bottom-style: solid;
66
62
  }
67
63
 
68
64
  /* Corner connector for RTL - combines vertical and horizontal */
69
65
  [data-connector-corner] {
70
66
  grid-column: 1;
71
67
  grid-row: 1;
72
- border-bottom-width: 1px;
73
- border-bottom-style: solid;
74
- border-right-width: 1px;
75
- border-right-style: solid;
76
68
  }
77
69
 
78
70
  /* =============================================================================
@@ -81,9 +73,6 @@
81
73
 
82
74
  [data-connector][data-connector-rtl] [data-connector-v] {
83
75
  grid-column: 2;
84
- border-right-width: 0;
85
- border-left-width: 1px;
86
- border-left-style: solid;
87
76
  }
88
77
 
89
78
  [data-connector][data-connector-rtl] [data-connector-h] {
@@ -92,9 +81,6 @@
92
81
 
93
82
  [data-connector][data-connector-rtl] [data-connector-corner] {
94
83
  grid-column: 2;
95
- border-right-width: 0;
96
- border-left-width: 1px;
97
- border-left-style: solid;
98
84
  }
99
85
 
100
86
  /* =============================================================================
@@ -0,0 +1,83 @@
1
+ /* Graph paper — minor + major grid background utility
2
+ *
3
+ * Override any variable on the element or a parent to customise:
4
+ * --unit minor cell size (default 0.5rem)
5
+ * --size major cell size (default calc(5 * --unit))
6
+ * --minor-grid minor line thickness (default 0.5px)
7
+ * --major-grid major line thickness (default 0.5px)
8
+ * --graph-paper-color line color (default currentColor)
9
+ */
10
+
11
+ /* Grid paper — simple H+V grid (no major/minor distinction)
12
+ *
13
+ * Override any variable on the element or a parent to customise:
14
+ * --unit cell size (default 0.5rem)
15
+ * --grid-line line thickness (default 0.5px)
16
+ * --grid-paper-color line color (default currentColor)
17
+ */
18
+
19
+ /* Ruled paper — horizontal lines only
20
+ *
21
+ * Override any variable on the element or a parent to customise:
22
+ * --unit line spacing (default 1.5rem)
23
+ * --rule-size line thickness (default 0.5px)
24
+ * --ruled-paper-color line color (default currentColor)
25
+ */
26
+
27
+ [data-graph-paper] {
28
+ background-image:
29
+ linear-gradient(
30
+ var(--graph-paper-color, currentColor) var(--major-grid, 0.5px),
31
+ transparent var(--major-grid, 0.5px)
32
+ ),
33
+ linear-gradient(
34
+ 90deg,
35
+ var(--graph-paper-color, currentColor) var(--major-grid, 0.5px),
36
+ transparent var(--major-grid, 0.5px)
37
+ ),
38
+ linear-gradient(
39
+ var(--graph-paper-color, currentColor) var(--minor-grid, 0.5px),
40
+ transparent var(--minor-grid, 0.5px)
41
+ ),
42
+ linear-gradient(
43
+ 90deg,
44
+ var(--graph-paper-color, currentColor) var(--minor-grid, 0.5px),
45
+ transparent var(--minor-grid, 0.5px)
46
+ );
47
+ background-size:
48
+ var(--size, calc(5 * var(--unit, 0.5rem))) var(--size, calc(5 * var(--unit, 0.5rem))),
49
+ var(--size, calc(5 * var(--unit, 0.5rem))) var(--size, calc(5 * var(--unit, 0.5rem))),
50
+ var(--unit, 0.5rem) var(--unit, 0.5rem),
51
+ var(--unit, 0.5rem) var(--unit, 0.5rem);
52
+ background-position:
53
+ calc(-1 * var(--minor-grid, 0.5px)) calc(-1 * var(--minor-grid, 0.5px)),
54
+ calc(-1 * var(--minor-grid, 0.5px)) calc(-1 * var(--minor-grid, 0.5px)),
55
+ calc(-1 * var(--minor-grid, 0.5px)) calc(-1 * var(--minor-grid, 0.5px)),
56
+ calc(-1 * var(--minor-grid, 0.5px)) calc(-1 * var(--minor-grid, 0.5px));
57
+ }
58
+
59
+ [data-grid-paper] {
60
+ background-image:
61
+ linear-gradient(
62
+ var(--grid-paper-color, currentColor) var(--grid-line, 0.5px),
63
+ transparent var(--grid-line, 0.5px)
64
+ ),
65
+ linear-gradient(
66
+ 90deg,
67
+ var(--grid-paper-color, currentColor) var(--grid-line, 0.5px),
68
+ transparent var(--grid-line, 0.5px)
69
+ );
70
+ background-size: var(--unit, 0.5rem) var(--unit, 0.5rem);
71
+ background-position:
72
+ calc(-1 * var(--grid-line, 0.5px)) calc(-1 * var(--grid-line, 0.5px)),
73
+ calc(-1 * var(--grid-line, 0.5px)) calc(-1 * var(--grid-line, 0.5px));
74
+ }
75
+
76
+ [data-ruled-paper] {
77
+ background-image: linear-gradient(
78
+ var(--ruled-paper-color, currentColor) var(--rule-size, 0.5px),
79
+ transparent var(--rule-size, 0.5px)
80
+ );
81
+ background-size: 100% var(--unit, 1.5rem);
82
+ background-position: 0 calc(-1 * var(--rule-size, 0.5px));
83
+ }
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Grid - Base Structural Styles
3
+ *
4
+ * Layout: CSS grid with auto-fill, tile padding/sizing.
5
+ * No colors or visual theming - those belong in theme styles.
6
+ */
7
+
8
+ /* =============================================================================
9
+ Grid Container
10
+ ============================================================================= */
11
+
12
+ [data-grid] {
13
+ display: grid;
14
+ grid-template-columns: repeat(auto-fill, minmax(var(--grid-min-size, 120px), 1fr));
15
+ gap: var(--grid-gap, 1rem);
16
+ }
17
+
18
+ [data-grid][data-disabled] {
19
+ pointer-events: none;
20
+ opacity: 0.5;
21
+ }
22
+
23
+ /* =============================================================================
24
+ Grid Tiles
25
+ ============================================================================= */
26
+
27
+ [data-grid-item] {
28
+ display: flex;
29
+ flex-direction: column;
30
+ align-items: center;
31
+ justify-content: center;
32
+ gap: 0.5rem;
33
+ padding: 1rem;
34
+ border: 1px solid transparent;
35
+ border-radius: 0.5rem;
36
+ cursor: pointer;
37
+ transition:
38
+ background-color 150ms ease,
39
+ border-color 150ms ease;
40
+ background: transparent;
41
+ text-align: center;
42
+ word-break: break-word;
43
+ }
44
+
45
+ [data-grid-item]:focus {
46
+ outline: none;
47
+ }
48
+
49
+ [data-grid-item][data-disabled],
50
+ [data-grid-item]:disabled {
51
+ pointer-events: none;
52
+ opacity: 0.5;
53
+ cursor: not-allowed;
54
+ }
55
+
56
+ /* =============================================================================
57
+ Size Variants
58
+ ============================================================================= */
59
+
60
+ [data-grid][data-size='sm'] [data-grid-item] {
61
+ padding: 0.5rem;
62
+ gap: 0.25rem;
63
+ font-size: 0.75rem;
64
+ }
65
+
66
+ [data-grid][data-size='md'] [data-grid-item],
67
+ [data-grid]:not([data-size]) [data-grid-item] {
68
+ padding: 1rem;
69
+ gap: 0.5rem;
70
+ font-size: 0.875rem;
71
+ }
72
+
73
+ [data-grid][data-size='lg'] [data-grid-item] {
74
+ padding: 1.5rem;
75
+ gap: 0.75rem;
76
+ font-size: 1rem;
77
+ }
78
+
79
+ /* =============================================================================
80
+ Item Elements (reuse shared data-item-* from item.css)
81
+ ============================================================================= */
82
+
83
+ [data-grid-item] [data-item-icon] {
84
+ font-size: 1.5rem;
85
+ }
86
+
87
+ [data-grid][data-size='sm'] [data-grid-item] [data-item-icon] {
88
+ font-size: 1.25rem;
89
+ }
90
+
91
+ [data-grid][data-size='lg'] [data-grid-item] [data-item-icon] {
92
+ font-size: 2rem;
93
+ }
@@ -33,4 +33,8 @@
33
33
  @import './range.css';
34
34
  @import './timeline.css';
35
35
  @import './reveal.css';
36
+ @import './graph-paper.css';
36
37
  @import './floating-navigation.css';
38
+ @import './grid.css';
39
+ @import './upload-target.css';
40
+ @import './upload-progress.css';
@@ -12,16 +12,16 @@
12
12
 
13
13
  /* Separator between form fields */
14
14
  [data-form-separator] {
15
- @apply border-t border-surface-z2 my-1;
15
+ @apply border-surface-z2 my-1 border-t;
16
16
  }
17
17
 
18
18
  /* Form group (fieldset for nested objects) */
19
19
  [data-form-group] {
20
- @apply flex flex-col gap-3 border-none m-0 p-0 pl-3 border-l border-surface-z2;
20
+ @apply border-surface-z2 m-0 flex flex-col gap-3 border-l border-none p-0 pl-3;
21
21
  }
22
22
 
23
23
  [data-form-group-label] {
24
- @apply text-xs font-semibold pb-1;
24
+ @apply pb-1 text-xs font-semibold;
25
25
  }
26
26
 
27
27
  /* Validation report */
@@ -34,11 +34,11 @@
34
34
  }
35
35
 
36
36
  [data-validation-group-header] {
37
- @apply flex items-center gap-1.5 text-xs font-semibold py-1;
37
+ @apply flex items-center gap-1.5 py-1 text-xs font-semibold;
38
38
  }
39
39
 
40
40
  [data-validation-count] {
41
- @apply inline-flex items-center justify-center rounded-full text-[10px] font-bold min-w-[18px] h-[18px] px-1;
41
+ @apply inline-flex h-[18px] min-w-[18px] items-center justify-center rounded-full px-1 text-[10px] font-bold;
42
42
  }
43
43
 
44
44
  [data-severity='error'] > [data-validation-group-header] {
@@ -74,21 +74,21 @@
74
74
  }
75
75
 
76
76
  [data-validation-item] {
77
- @apply text-xs py-0.5 px-2 rounded text-left;
77
+ @apply rounded px-2 py-0.5 text-left text-xs;
78
78
  }
79
79
 
80
80
  button[data-validation-item] {
81
- @apply cursor-pointer bg-transparent border-none hover:bg-surface-z1;
81
+ @apply hover:bg-surface-z1 cursor-pointer border-none bg-transparent;
82
82
  }
83
83
 
84
84
  /* Form actions (submit/reset buttons) */
85
85
  [data-form-actions] {
86
- @apply flex flex-row gap-2 justify-end pt-2;
86
+ @apply flex flex-row justify-end gap-2 pt-2;
87
87
  }
88
88
 
89
89
  button[data-form-submit],
90
90
  button[data-form-reset] {
91
- @apply px-4 py-1.5 text-sm rounded cursor-pointer border border-surface-z3 bg-surface-z1 hover:bg-surface-z2 transition-colors;
91
+ @apply border-surface-z3 bg-surface-z1 hover:bg-surface-z2 cursor-pointer rounded border px-4 py-1.5 text-sm transition-colors;
92
92
  }
93
93
 
94
94
  button[data-form-submit] {
@@ -97,12 +97,12 @@ button[data-form-submit] {
97
97
 
98
98
  button[data-form-submit]:disabled,
99
99
  button[data-form-reset]:disabled {
100
- @apply opacity-50 cursor-not-allowed;
100
+ @apply cursor-not-allowed opacity-50;
101
101
  }
102
102
 
103
103
  /* Submitting state: reduce interactivity */
104
104
  [data-form-submitting] > :not([data-form-actions]) {
105
- @apply opacity-60 pointer-events-none;
105
+ @apply pointer-events-none opacity-60;
106
106
  }
107
107
 
108
108
  /* Field root container */
@@ -148,7 +148,7 @@ button[data-form-reset]:disabled {
148
148
  }
149
149
 
150
150
  [data-field-info] {
151
- @apply text-xs font-mono;
151
+ @apply font-mono text-xs;
152
152
  }
153
153
 
154
154
  /* Numeric/date input alignment */
@@ -219,6 +219,10 @@ input[type='color'] {
219
219
  @apply p-0;
220
220
  }
221
221
 
222
+ [data-input-root] [data-input-icon] {
223
+ @apply mx-2 aspect-square min-h-full flex-shrink-0;
224
+ }
225
+
222
226
  /* Responsive adjustments */
223
227
  @media (max-width: 640px) {
224
228
  [data-field-root] {
package/src/base/item.css CHANGED
@@ -13,6 +13,18 @@
13
13
  flex-shrink: 0;
14
14
  }
15
15
 
16
+ /* =============================================================================
17
+ Item Avatar
18
+ ============================================================================= */
19
+
20
+ [data-item-avatar] {
21
+ flex-shrink: 0;
22
+ width: 1.5em;
23
+ height: 1.5em;
24
+ border-radius: 9999px;
25
+ object-fit: cover;
26
+ }
27
+
16
28
  /* =============================================================================
17
29
  Item Text Container
18
30
  ============================================================================= */
@@ -50,3 +62,17 @@
50
62
  font-weight: 500;
51
63
  border-radius: 9999px;
52
64
  }
65
+
66
+ /* =============================================================================
67
+ Item Shortcut
68
+ ============================================================================= */
69
+
70
+ [data-item-shortcut] {
71
+ flex-shrink: 0;
72
+ margin-left: auto;
73
+ padding: 0.125rem 0.375rem;
74
+ font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
75
+ font-size: 0.75em;
76
+ font-weight: 500;
77
+ border-radius: 0.25rem;
78
+ }