@rokkit/themes 1.1.15 → 1.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. package/build.mjs +71 -1
  2. package/dist/base/alert-list.css +91 -0
  3. package/dist/base/avatar.css +82 -0
  4. package/dist/base/badge.css +41 -0
  5. package/dist/base/breadcrumbs.css +47 -0
  6. package/dist/base/button.css +254 -0
  7. package/dist/base/card.css +39 -0
  8. package/dist/base/carousel.css +128 -0
  9. package/dist/base/chart.css +94 -0
  10. package/dist/base/code-block.css +76 -0
  11. package/dist/base/command-palette.css +35 -0
  12. package/dist/base/connector.css +92 -0
  13. package/dist/base/density.css +60 -0
  14. package/dist/base/display.css +99 -0
  15. package/dist/base/divider.css +49 -0
  16. package/dist/base/dropdown.css +167 -0
  17. package/dist/base/floating-action.css +388 -0
  18. package/dist/base/floating-navigation.css +405 -0
  19. package/dist/base/frame.css +36 -0
  20. package/dist/base/gradient-border.css +32 -0
  21. package/dist/base/graph-paper.css +83 -0
  22. package/dist/base/grid.css +93 -0
  23. package/dist/base/input.css +342 -0
  24. package/dist/base/item.css +91 -0
  25. package/dist/base/layout.css +17 -0
  26. package/dist/base/list.css +188 -0
  27. package/dist/base/menu.css +277 -0
  28. package/dist/base/message.css +62 -0
  29. package/dist/base/nav-content.css +48 -0
  30. package/dist/base/palette-manager.css +244 -0
  31. package/dist/base/pill.css +61 -0
  32. package/dist/base/progress.css +34 -0
  33. package/dist/base/radius.css +41 -0
  34. package/dist/base/range.css +122 -0
  35. package/dist/base/rating.css +42 -0
  36. package/dist/base/responsive-grid.css +8 -0
  37. package/dist/base/reveal.css +37 -0
  38. package/dist/base/search-filter.css +132 -0
  39. package/dist/base/select.css +413 -0
  40. package/dist/base/shine.css +14 -0
  41. package/dist/base/stack.css +76 -0
  42. package/dist/base/status-list.css +19 -0
  43. package/dist/base/step-indicator.css +74 -0
  44. package/dist/base/stepper.css +148 -0
  45. package/dist/base/swatch.css +85 -0
  46. package/dist/base/switch.css +168 -0
  47. package/dist/base/table.css +310 -0
  48. package/dist/base/tabs.css +184 -0
  49. package/dist/base/tilt.css +14 -0
  50. package/dist/base/timeline.css +84 -0
  51. package/dist/base/toc.css +50 -0
  52. package/dist/base/toggle.css +185 -0
  53. package/dist/base/toolbar.css +341 -0
  54. package/dist/base/tooltip.css +64 -0
  55. package/dist/base/tree.css +235 -0
  56. package/dist/base/typography.css +50 -0
  57. package/dist/base/upload-progress.css +155 -0
  58. package/dist/base/upload-target.css +77 -0
  59. package/dist/base.css +7206 -0
  60. package/dist/frosted/button.css +228 -0
  61. package/dist/frosted/card.css +128 -0
  62. package/dist/frosted/chart.css +38 -0
  63. package/dist/frosted/code-block.css +33 -0
  64. package/dist/frosted/command-palette.css +43 -0
  65. package/dist/frosted/dropdown.css +64 -0
  66. package/dist/frosted/floating-action.css +66 -0
  67. package/dist/frosted/floating-navigation.css +78 -0
  68. package/dist/frosted/frame.css +17 -0
  69. package/dist/frosted/grid.css +26 -0
  70. package/dist/frosted/input.css +135 -0
  71. package/dist/frosted/list.css +153 -0
  72. package/dist/frosted/menu.css +107 -0
  73. package/dist/frosted/message.css +39 -0
  74. package/dist/frosted/palette-manager.css +97 -0
  75. package/dist/frosted/pill.css +17 -0
  76. package/dist/frosted/progress.css +13 -0
  77. package/dist/frosted/range.css +64 -0
  78. package/dist/frosted/rating.css +13 -0
  79. package/dist/frosted/search-filter.css +53 -0
  80. package/dist/frosted/select.css +192 -0
  81. package/dist/frosted/status-list.css +67 -0
  82. package/dist/frosted/step-indicator.css +43 -0
  83. package/dist/frosted/swatch.css +21 -0
  84. package/dist/frosted/switch.css +43 -0
  85. package/dist/frosted/table.css +122 -0
  86. package/dist/frosted/tabs.css +61 -0
  87. package/dist/frosted/timeline.css +46 -0
  88. package/dist/frosted/toc.css +19 -0
  89. package/dist/frosted/toggle.css +77 -0
  90. package/dist/frosted/toolbar.css +92 -0
  91. package/dist/frosted/tooltip.css +7 -0
  92. package/dist/frosted/tree.css +115 -0
  93. package/dist/frosted.css +2357 -0
  94. package/dist/index.css +19199 -0
  95. package/dist/material/button.css +178 -0
  96. package/dist/material/card.css +99 -0
  97. package/dist/material/chart.css +38 -0
  98. package/dist/material/code-block.css +33 -0
  99. package/dist/material/command-palette.css +37 -0
  100. package/dist/material/dropdown.css +50 -0
  101. package/dist/material/floating-action.css +64 -0
  102. package/dist/material/floating-navigation.css +74 -0
  103. package/dist/material/frame.css +17 -0
  104. package/dist/material/grid.css +26 -0
  105. package/dist/material/input.css +164 -0
  106. package/dist/material/list.css +140 -0
  107. package/dist/material/menu.css +92 -0
  108. package/dist/material/message.css +35 -0
  109. package/dist/material/palette-manager.css +97 -0
  110. package/dist/material/pill.css +17 -0
  111. package/dist/material/progress.css +13 -0
  112. package/dist/material/range.css +62 -0
  113. package/dist/material/rating.css +13 -0
  114. package/dist/material/search-filter.css +49 -0
  115. package/dist/material/select.css +177 -0
  116. package/dist/material/status-list.css +66 -0
  117. package/dist/material/step-indicator.css +40 -0
  118. package/dist/material/swatch.css +21 -0
  119. package/dist/material/switch.css +28 -0
  120. package/dist/material/table.css +120 -0
  121. package/dist/material/tabs.css +88 -0
  122. package/dist/material/timeline.css +45 -0
  123. package/dist/material/toc.css +18 -0
  124. package/dist/material/toggle.css +74 -0
  125. package/dist/material/toolbar.css +85 -0
  126. package/dist/material/tooltip.css +7 -0
  127. package/dist/material/tree.css +134 -0
  128. package/dist/material.css +2241 -0
  129. package/dist/minimal/button.css +176 -0
  130. package/dist/minimal/card.css +99 -0
  131. package/dist/minimal/chart.css +38 -0
  132. package/dist/minimal/code-block.css +33 -0
  133. package/dist/minimal/command-palette.css +37 -0
  134. package/dist/minimal/dropdown.css +50 -0
  135. package/dist/minimal/floating-action.css +63 -0
  136. package/dist/minimal/floating-navigation.css +70 -0
  137. package/dist/minimal/frame.css +17 -0
  138. package/dist/minimal/grid.css +26 -0
  139. package/dist/minimal/input.css +171 -0
  140. package/dist/minimal/list.css +168 -0
  141. package/dist/minimal/menu.css +88 -0
  142. package/dist/minimal/message.css +35 -0
  143. package/dist/minimal/palette-manager.css +97 -0
  144. package/dist/minimal/pill.css +17 -0
  145. package/dist/minimal/progress.css +13 -0
  146. package/dist/minimal/range.css +61 -0
  147. package/dist/minimal/rating.css +13 -0
  148. package/dist/minimal/search-filter.css +49 -0
  149. package/dist/minimal/select.css +158 -0
  150. package/dist/minimal/status-list.css +66 -0
  151. package/dist/minimal/step-indicator.css +40 -0
  152. package/dist/minimal/swatch.css +21 -0
  153. package/dist/minimal/switch.css +32 -0
  154. package/dist/minimal/table.css +118 -0
  155. package/dist/minimal/tabs.css +151 -0
  156. package/dist/minimal/timeline.css +45 -0
  157. package/dist/minimal/toc.css +18 -0
  158. package/dist/minimal/toggle.css +74 -0
  159. package/dist/minimal/toolbar.css +85 -0
  160. package/dist/minimal/tooltip.css +7 -0
  161. package/dist/minimal/tree.css +141 -0
  162. package/dist/minimal.css +2317 -0
  163. package/dist/rokkit/avatar.css +29 -0
  164. package/dist/rokkit/badge.css +29 -0
  165. package/dist/rokkit/button.css +308 -0
  166. package/dist/rokkit/card.css +102 -0
  167. package/dist/rokkit/chart.css +38 -0
  168. package/dist/rokkit/code-block.css +33 -0
  169. package/dist/rokkit/command-palette.css +37 -0
  170. package/dist/rokkit/connector.css +11 -0
  171. package/dist/rokkit/divider.css +26 -0
  172. package/dist/rokkit/dropdown.css +70 -0
  173. package/dist/rokkit/floating-action.css +65 -0
  174. package/dist/rokkit/floating-navigation.css +83 -0
  175. package/dist/rokkit/frame.css +17 -0
  176. package/dist/rokkit/grid.css +46 -0
  177. package/dist/rokkit/input.css +134 -0
  178. package/dist/rokkit/list.css +202 -0
  179. package/dist/rokkit/menu.css +93 -0
  180. package/dist/rokkit/message.css +44 -0
  181. package/dist/rokkit/palette-manager.css +97 -0
  182. package/dist/rokkit/pill.css +17 -0
  183. package/dist/rokkit/progress.css +13 -0
  184. package/dist/rokkit/range.css +62 -0
  185. package/dist/rokkit/rating.css +13 -0
  186. package/dist/rokkit/search-filter.css +49 -0
  187. package/dist/rokkit/select.css +190 -0
  188. package/dist/rokkit/status-list.css +68 -0
  189. package/dist/rokkit/step-indicator.css +40 -0
  190. package/dist/rokkit/swatch.css +20 -0
  191. package/dist/rokkit/switch.css +29 -0
  192. package/dist/rokkit/table.css +118 -0
  193. package/dist/rokkit/tabs.css +114 -0
  194. package/dist/rokkit/timeline.css +45 -0
  195. package/dist/rokkit/toc.css +18 -0
  196. package/dist/rokkit/toggle.css +85 -0
  197. package/dist/rokkit/toolbar.css +90 -0
  198. package/dist/rokkit/tooltip.css +7 -0
  199. package/dist/rokkit/tree.css +149 -0
  200. package/dist/rokkit/upload-progress.css +102 -0
  201. package/dist/rokkit/upload-target.css +50 -0
  202. package/dist/rokkit.css +2789 -0
  203. package/dist/zen-sumi/button.css +176 -0
  204. package/dist/zen-sumi/card.css +104 -0
  205. package/dist/zen-sumi/chart.css +41 -0
  206. package/dist/zen-sumi/code-block.css +35 -0
  207. package/dist/zen-sumi/command-palette.css +39 -0
  208. package/dist/zen-sumi/dropdown.css +53 -0
  209. package/dist/zen-sumi/floating-action.css +68 -0
  210. package/dist/zen-sumi/floating-navigation.css +74 -0
  211. package/dist/zen-sumi/frame.css +20 -0
  212. package/dist/zen-sumi/grid.css +26 -0
  213. package/dist/zen-sumi/input.css +171 -0
  214. package/dist/zen-sumi/list.css +128 -0
  215. package/dist/zen-sumi/menu.css +91 -0
  216. package/dist/zen-sumi/message.css +37 -0
  217. package/dist/zen-sumi/palette-manager.css +97 -0
  218. package/dist/zen-sumi/pill.css +17 -0
  219. package/dist/zen-sumi/progress.css +13 -0
  220. package/dist/zen-sumi/range.css +62 -0
  221. package/dist/zen-sumi/rating.css +13 -0
  222. package/dist/zen-sumi/search-filter.css +49 -0
  223. package/dist/zen-sumi/select.css +160 -0
  224. package/dist/zen-sumi/status-list.css +66 -0
  225. package/dist/zen-sumi/step-indicator.css +40 -0
  226. package/dist/zen-sumi/swatch.css +21 -0
  227. package/dist/zen-sumi/switch.css +34 -0
  228. package/dist/zen-sumi/table.css +128 -0
  229. package/dist/zen-sumi/tabs.css +78 -0
  230. package/dist/zen-sumi/timeline.css +46 -0
  231. package/dist/zen-sumi/toc.css +22 -0
  232. package/dist/zen-sumi/toggle.css +85 -0
  233. package/dist/zen-sumi/toolbar.css +86 -0
  234. package/dist/zen-sumi/tooltip.css +7 -0
  235. package/dist/zen-sumi/tree.css +137 -0
  236. package/dist/zen-sumi.css +2284 -0
  237. package/package.json +45 -43
  238. package/src/frosted/button.css +12 -3
  239. package/src/frosted/card.css +5 -3
  240. package/src/frosted/command-palette.css +2 -1
  241. package/src/frosted/dropdown.css +6 -3
  242. package/src/frosted/floating-action.css +10 -5
  243. package/src/frosted/floating-navigation.css +7 -3
  244. package/src/frosted/input.css +4 -3
  245. package/src/frosted/list.css +12 -6
  246. package/src/frosted/menu.css +6 -3
  247. package/src/frosted/message.css +8 -4
  248. package/src/frosted/range.css +7 -4
  249. package/src/frosted/search-filter.css +7 -3
  250. package/src/frosted/select.css +26 -12
  251. package/src/frosted/status-list.css +2 -1
  252. package/src/frosted/step-indicator.css +6 -3
  253. package/src/frosted/switch.css +2 -1
  254. package/src/frosted/table.css +12 -6
  255. package/src/frosted/tabs.css +5 -2
  256. package/src/frosted/timeline.css +3 -2
  257. package/src/frosted/toc.css +2 -1
  258. package/src/frosted/toggle.css +5 -2
  259. package/src/frosted/toolbar.css +17 -9
  260. package/src/frosted/tree.css +10 -5
package/build.mjs CHANGED
@@ -20,7 +20,7 @@
20
20
  import { createGenerator, presetWind3, transformerDirectives } from 'unocss'
21
21
  import { Theme } from '@rokkit/core'
22
22
  import { buildNamedShortcuts } from '@rokkit/unocss'
23
- import { readFileSync, writeFileSync, mkdirSync } from 'fs'
23
+ import { readFileSync, writeFileSync, mkdirSync, readdirSync } from 'fs'
24
24
  import { resolve, dirname, join } from 'path'
25
25
  import { fileURLToPath } from 'url'
26
26
  import MagicString from 'magic-string'
@@ -178,19 +178,79 @@ function splitTopLevelSelectors(text) {
178
178
  return parts
179
179
  }
180
180
 
181
+ // ─── Regression guard: no @apply may survive into dist ────────────────────────
182
+
183
+ /**
184
+ * Strip /* … *​/ block comments so the scan matches real CSS, not prose
185
+ * (some files document the @apply pitfall in comments). Mirrors the
186
+ * `stripComments` helper in spec/coverage.spec.js.
187
+ */
188
+ const stripComments = (s) => s.replace(/\/\*[\s\S]*?\*\//g, '')
189
+
190
+ /**
191
+ * Fail the build if any real `@apply` directive survived into a dist file.
192
+ *
193
+ * A leftover `@apply` means a utility didn't resolve (e.g. a named-token
194
+ * `/opacity` shortcut, which UnoCSS can't expand) and would ship raw to
195
+ * consumers — triggering `[lightningcss minify] Unknown at rule: @apply`
196
+ * and rendering nothing. This is the recurrence guard for issue #135.
197
+ */
198
+ function assertNoApply(outputName) {
199
+ const content = readFileSync(join(distDir, outputName), 'utf-8') // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal — outputName is derived from a hardcoded string array, not user input
200
+ const lines = stripComments(content).split('\n')
201
+ const offending = []
202
+ lines.forEach((line, i) => {
203
+ if (/@apply\b/.test(line)) offending.push(` dist/${outputName}:${i + 1} ${line.trim()}`)
204
+ })
205
+ if (offending.length > 0) {
206
+ throw new Error(
207
+ `Unresolved @apply leaked into dist/${outputName} (issue #135 recurrence).\n` +
208
+ `These utilities did not resolve during build — rewrite them to raw CSS ` +
209
+ `(e.g. color-mix for named-token /opacity):\n${offending.join('\n')}`
210
+ )
211
+ }
212
+ }
213
+
181
214
  // ─── Build ────────────────────────────────────────────────────────────────────
182
215
 
183
216
  const srcDir = join(__dirname, 'src')
184
217
  const distDir = join(__dirname, 'dist')
185
218
 
219
+ /** Names of every dist file emitted by this build, scanned by the guard. */
220
+ const emitted = []
221
+
186
222
  async function buildFile(inputPath, outputName, label) {
187
223
  const fullCSS = resolveImports(inputPath)
188
224
  const compiled = await processCSS(fullCSS, outputName)
189
225
  const fixed = fixModeSelectors(compiled)
190
226
  writeFileSync(join(distDir, outputName), fixed, 'utf-8') // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal — outputName is derived from a hardcoded string array, not user input
227
+ emitted.push(outputName)
191
228
  console.log(`✓ dist/${outputName} (${label})`)
192
229
  }
193
230
 
231
+ /**
232
+ * Compile every per-component CSS file (e.g. src/frosted/button.css) into
233
+ * dist/<style>/<component>.css, expanding @apply just like the bundled files.
234
+ *
235
+ * Consumers that import a single component path (e.g.
236
+ * `@rokkit/themes/frosted/button.css`) get resolved CSS, not raw @apply — the
237
+ * `./<style>/*` exports point here. index.css is skipped (it is only an
238
+ * aggregator of @imports, already compiled into dist/<style>.css).
239
+ */
240
+ async function buildComponentFiles(style) {
241
+ mkdirSync(join(distDir, style), { recursive: true }) // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal — style is from a hardcoded string array, not user input
242
+ const files = readdirSync(join(srcDir, style)) // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal — style is from a hardcoded string array, not user input
243
+ .filter((f) => f.endsWith('.css') && f !== 'index.css')
244
+ .sort()
245
+ for (const file of files) {
246
+ const outputName = `${style}/${file}`
247
+ const compiled = await processCSS(readFileSync(join(srcDir, style, file), 'utf-8'), outputName) // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal — style/file enumerate a hardcoded src dir, not user input
248
+ writeFileSync(join(distDir, outputName), fixModeSelectors(compiled), 'utf-8') // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal — derived from enumerated src files, not user input
249
+ emitted.push(outputName)
250
+ }
251
+ console.log(`✓ dist/${style}/*.css (${files.length} components)`)
252
+ }
253
+
194
254
  async function build() {
195
255
  mkdirSync(distDir, { recursive: true })
196
256
 
@@ -202,6 +262,7 @@ async function build() {
202
262
  const compiledBase = await processCSS(baseCSS, 'base.css')
203
263
  const baseFull = fixModeSelectors(compiledPalette + '\n' + zScaleCSS + '\n' + compiledBase)
204
264
  writeFileSync(join(distDir, 'base.css'), baseFull, 'utf-8')
265
+ emitted.push('base.css')
205
266
  console.log('✓ dist/base.css (structural styles + palette defaults)')
206
267
 
207
268
  // Per-theme files
@@ -215,6 +276,11 @@ async function build() {
215
276
  await buildFile(join(srcDir, name, 'index.css'), `${name}.css`, label)
216
277
  }
217
278
 
279
+ // Per-component files: dist/<style>/<component>.css (for `./<style>/*` exports)
280
+ for (const style of ['base', 'rokkit', 'minimal', 'material', 'frosted', 'zen-sumi']) {
281
+ await buildComponentFiles(style)
282
+ }
283
+
218
284
  // Full bundle: base + all themes
219
285
  const allThemes = [
220
286
  'base',
@@ -227,8 +293,12 @@ async function build() {
227
293
  // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal — name is from a hardcoded string array, not user input
228
294
  const bundleParts = allThemes.map((name) => readFileSync(join(distDir, `${name}.css`), 'utf-8'))
229
295
  writeFileSync(join(distDir, 'index.css'), bundleParts.join('\n'), 'utf-8')
296
+ emitted.push('index.css')
230
297
  console.log('✓ dist/index.css (full bundle)')
231
298
 
299
+ // Regression guard (#135): no unresolved @apply may ship in dist.
300
+ for (const outputName of emitted) assertNoApply(outputName)
301
+
232
302
  console.log('\n@rokkit/themes build complete.')
233
303
  }
234
304
 
@@ -0,0 +1,91 @@
1
+ /* AlertList — base structural styles for the fixed toast container */
2
+
3
+ [data-alert-list] {
4
+ position: fixed;
5
+ z-index: 9999;
6
+ display: flex;
7
+ flex-direction: column;
8
+ max-width: 24rem;
9
+ width: calc(100% - 2rem);
10
+ padding: 1rem;
11
+ pointer-events: none;
12
+ }
13
+
14
+ /* Each toast wrapper: handles enter animation, spacing, and dismiss collapse */
15
+ [data-alert-list] > * {
16
+ pointer-events: auto;
17
+ overflow: hidden;
18
+ max-height: 12rem;
19
+ margin-bottom: 0.5rem;
20
+ transition:
21
+ max-height 0.25s ease-out,
22
+ margin-bottom 0.25s ease-out;
23
+ }
24
+
25
+ /* Collapse height + spacing when dismissing — CSS drives the animation */
26
+ [data-alert-list] > *[data-dismissing] {
27
+ max-height: 0;
28
+ margin-bottom: 0;
29
+ }
30
+
31
+ /* Message enter animation */
32
+ @keyframes toast-enter {
33
+ from {
34
+ opacity: 0;
35
+ transform: translateX(2rem);
36
+ }
37
+ to {
38
+ opacity: 1;
39
+ transform: translateX(0);
40
+ }
41
+ }
42
+
43
+ /* Message exit transition */
44
+ [data-alert-list] > * > [data-message-root] {
45
+ animation: toast-enter 0.2s ease-out;
46
+ transition:
47
+ opacity 0.2s ease-out,
48
+ transform 0.2s ease-out;
49
+ }
50
+
51
+ [data-alert-list] > *[data-dismissing] > [data-message-root] {
52
+ opacity: 0;
53
+ transform: translateX(2rem);
54
+ }
55
+
56
+ /* Top positions */
57
+ [data-alert-list][data-position='top-right'] {
58
+ top: 0;
59
+ right: 0;
60
+ }
61
+
62
+ [data-alert-list][data-position='top-center'] {
63
+ top: 0;
64
+ left: 50%;
65
+ transform: translateX(-50%);
66
+ }
67
+
68
+ [data-alert-list][data-position='top-left'] {
69
+ top: 0;
70
+ left: 0;
71
+ }
72
+
73
+ /* Bottom positions — reverse stack so newest appears at bottom edge */
74
+ [data-alert-list][data-position='bottom-right'] {
75
+ bottom: 0;
76
+ right: 0;
77
+ flex-direction: column-reverse;
78
+ }
79
+
80
+ [data-alert-list][data-position='bottom-center'] {
81
+ bottom: 0;
82
+ left: 50%;
83
+ transform: translateX(-50%);
84
+ flex-direction: column-reverse;
85
+ }
86
+
87
+ [data-alert-list][data-position='bottom-left'] {
88
+ bottom: 0;
89
+ left: 0;
90
+ flex-direction: column-reverse;
91
+ }
@@ -0,0 +1,82 @@
1
+ /* Avatar — base structural styles */
2
+
3
+ [data-avatar] {
4
+ position: relative;
5
+ display: inline-flex;
6
+ align-items: center;
7
+ justify-content: center;
8
+ flex-shrink: 0;
9
+ overflow: hidden;
10
+ user-select: none;
11
+ }
12
+
13
+ /* Shape */
14
+ [data-avatar][data-shape='circle'] {
15
+ border-radius: 9999px;
16
+ }
17
+
18
+ [data-avatar][data-shape='square'] {
19
+ border-radius: 0.375rem;
20
+ }
21
+
22
+ /* Size variants */
23
+ [data-avatar][data-size='xs'] {
24
+ width: 1.5rem;
25
+ height: 1.5rem;
26
+ font-size: 0.5rem;
27
+ }
28
+
29
+ [data-avatar][data-size='sm'] {
30
+ width: 2rem;
31
+ height: 2rem;
32
+ font-size: 0.625rem;
33
+ }
34
+
35
+ [data-avatar][data-size='md'] {
36
+ width: 2.5rem;
37
+ height: 2.5rem;
38
+ font-size: 0.75rem;
39
+ }
40
+
41
+ [data-avatar][data-size='lg'] {
42
+ width: 3rem;
43
+ height: 3rem;
44
+ font-size: 0.875rem;
45
+ }
46
+
47
+ [data-avatar][data-size='xl'] {
48
+ width: 4rem;
49
+ height: 4rem;
50
+ font-size: 1.125rem;
51
+ }
52
+
53
+ [data-avatar-img] {
54
+ width: 100%;
55
+ height: 100%;
56
+ object-fit: cover;
57
+ }
58
+
59
+ [data-avatar-initials] {
60
+ font-weight: 600;
61
+ letter-spacing: 0.02em;
62
+ line-height: 1;
63
+ }
64
+
65
+ /* Status indicator dot */
66
+ [data-avatar-status] {
67
+ position: absolute;
68
+ bottom: 0;
69
+ right: 0;
70
+ width: 25%;
71
+ height: 25%;
72
+ min-width: 0.5rem;
73
+ min-height: 0.5rem;
74
+ border-radius: 9999px;
75
+ border: 2px solid transparent;
76
+ }
77
+
78
+ [data-avatar][data-shape='square'] [data-avatar-status] {
79
+ border-radius: 9999px;
80
+ bottom: -0.125rem;
81
+ right: -0.125rem;
82
+ }
@@ -0,0 +1,41 @@
1
+ /* Badge — base structural styles */
2
+
3
+ [data-badge-wrapper] {
4
+ position: relative;
5
+ display: inline-flex;
6
+ }
7
+
8
+ [data-badge] {
9
+ display: inline-flex;
10
+ align-items: center;
11
+ justify-content: center;
12
+ min-width: 1.25rem;
13
+ height: 1.25rem;
14
+ padding: 0 0.375rem;
15
+ border-radius: 9999px;
16
+ font-size: 0.6875rem;
17
+ font-weight: 600;
18
+ line-height: 1;
19
+ white-space: nowrap;
20
+ }
21
+
22
+ /* Dot mode — small circle with no content */
23
+ [data-badge][data-dot] {
24
+ min-width: 0.5rem;
25
+ width: 0.5rem;
26
+ height: 0.5rem;
27
+ padding: 0;
28
+ }
29
+
30
+ /* Positioned inside a wrapper */
31
+ [data-badge-wrapper] [data-badge] {
32
+ position: absolute;
33
+ top: -0.375rem;
34
+ right: -0.375rem;
35
+ z-index: 1;
36
+ }
37
+
38
+ [data-badge-wrapper] [data-badge][data-dot] {
39
+ top: -0.125rem;
40
+ right: -0.125rem;
41
+ }
@@ -0,0 +1,47 @@
1
+ /* BreadCrumbs — base structural styles */
2
+
3
+ [data-breadcrumbs] {
4
+ display: block;
5
+ }
6
+
7
+ [data-breadcrumb-list] {
8
+ display: flex;
9
+ align-items: center;
10
+ list-style: none;
11
+ padding: 0;
12
+ margin: 0;
13
+ gap: 0.25rem;
14
+ }
15
+
16
+ [data-breadcrumb-item] {
17
+ display: flex;
18
+ align-items: center;
19
+ }
20
+
21
+ [data-breadcrumb-separator] {
22
+ display: flex;
23
+ align-items: center;
24
+ opacity: 0.5;
25
+ }
26
+
27
+ [data-breadcrumb-link] {
28
+ display: flex;
29
+ align-items: center;
30
+ gap: 0.25rem;
31
+ background: none;
32
+ border: none;
33
+ cursor: pointer;
34
+ padding: 0.25rem 0.5rem;
35
+ border-radius: 0.25rem;
36
+ font: inherit;
37
+ color: inherit;
38
+ text-decoration: none;
39
+ }
40
+
41
+ [data-breadcrumb-current] {
42
+ display: flex;
43
+ align-items: center;
44
+ gap: 0.25rem;
45
+ padding: 0.25rem 0.5rem;
46
+ font-weight: 600;
47
+ }
@@ -0,0 +1,254 @@
1
+ /**
2
+ * Button - Base Structural Styles
3
+ *
4
+ * These styles provide layout, positioning, and basic structure.
5
+ * No colors, shadows, or visual theming - those belong in theme styles.
6
+ */
7
+
8
+ /* =============================================================================
9
+ Button
10
+ ============================================================================= */
11
+
12
+ [data-button] {
13
+ display: inline-flex;
14
+ align-items: center;
15
+ justify-content: center;
16
+ gap: 0.5rem;
17
+ border-radius: 0.5rem;
18
+ font-weight: 500;
19
+ cursor: pointer;
20
+ border: 1px solid transparent;
21
+ white-space: nowrap;
22
+ user-select: none;
23
+ text-decoration: none;
24
+ transition: all 150ms ease;
25
+ background: transparent;
26
+ color: inherit;
27
+ font-family: inherit;
28
+ line-height: 1;
29
+ }
30
+
31
+ [data-button]:focus {
32
+ outline: none;
33
+ }
34
+
35
+ [data-button]:focus-visible {
36
+ outline: 2px solid currentColor;
37
+ outline-offset: 2px;
38
+ }
39
+
40
+ /* Disabled */
41
+ [data-button][data-disabled],
42
+ [data-button]:disabled {
43
+ pointer-events: none;
44
+ opacity: 0.5;
45
+ cursor: not-allowed;
46
+ }
47
+
48
+ /* Loading */
49
+ [data-button][data-loading] {
50
+ pointer-events: none;
51
+ position: relative;
52
+ }
53
+
54
+ /* Icon-only */
55
+ [data-button][data-icon-only] {
56
+ aspect-ratio: 1;
57
+ padding: 0;
58
+ }
59
+
60
+ [data-button][data-icon-only] [data-item-text] {
61
+ display: none;
62
+ }
63
+
64
+ /* =============================================================================
65
+ Size Variants
66
+ ============================================================================= */
67
+
68
+ /* Small */
69
+ [data-button][data-size='sm'] {
70
+ height: var(--control-h-sm);
71
+ padding-inline: 0.625rem;
72
+ font-size: 0.75rem;
73
+ gap: 0.375rem;
74
+ }
75
+
76
+ [data-button][data-size='sm'][data-icon-only] {
77
+ width: var(--control-h-sm);
78
+ }
79
+
80
+ [data-button][data-size='sm'] [data-item-icon],
81
+ [data-button][data-size='sm'] [data-button-icon-right] {
82
+ font-size: 1.25rem;
83
+ }
84
+
85
+ /* Medium (default) — responds to density */
86
+ [data-button][data-size='md'],
87
+ [data-button]:not([data-size]) {
88
+ height: var(--control-h-md);
89
+ padding-inline: var(--density-spacing-lg);
90
+ font-size: 0.875rem;
91
+ gap: var(--density-spacing-sm);
92
+ }
93
+
94
+ [data-button][data-size='md'][data-icon-only],
95
+ [data-button]:not([data-size])[data-icon-only] {
96
+ width: var(--control-h-md);
97
+ }
98
+
99
+ [data-button][data-size='md'] [data-item-icon],
100
+ [data-button][data-size='md'] [data-button-icon-right],
101
+ [data-button]:not([data-size]) [data-item-icon],
102
+ [data-button]:not([data-size]) [data-button-icon-right] {
103
+ font-size: var(--density-icon-size);
104
+ }
105
+
106
+ /* Large */
107
+ [data-button][data-size='lg'] {
108
+ height: var(--control-h-lg);
109
+ padding-inline: 1.5rem;
110
+ font-size: 1rem;
111
+ gap: 0.625rem;
112
+ }
113
+
114
+ [data-button][data-size='lg'][data-icon-only] {
115
+ width: var(--control-h-lg);
116
+ }
117
+
118
+ [data-button][data-size='lg'] [data-item-icon],
119
+ [data-button][data-size='lg'] [data-button-icon-right] {
120
+ font-size: 1.875rem;
121
+ }
122
+
123
+ /* =============================================================================
124
+ Button Elements
125
+ ============================================================================= */
126
+
127
+ [data-button] [data-item-icon],
128
+ [data-button] [data-button-icon-right] {
129
+ flex-shrink: 0;
130
+ }
131
+
132
+ [data-button] [data-item-text] {
133
+ display: inline-flex;
134
+ align-items: center;
135
+ }
136
+
137
+ /* Hide description and badge inside buttons (ItemContent renders them but we don't need them) */
138
+ [data-button] [data-item-description],
139
+ [data-button] [data-item-badge] {
140
+ display: none;
141
+ }
142
+
143
+ /* =============================================================================
144
+ Gradient Style — Structural
145
+ ============================================================================= */
146
+
147
+ [data-button][data-style='gradient'] {
148
+ border: none;
149
+ }
150
+
151
+ /* =============================================================================
152
+ Link Style — Structural
153
+ ============================================================================= */
154
+
155
+ [data-button][data-style='link'] {
156
+ background: transparent;
157
+ border: none;
158
+ height: auto;
159
+ padding-inline: 0.25rem;
160
+ border-radius: 0;
161
+ font-weight: inherit;
162
+ }
163
+
164
+ [data-button][data-style='link']:hover:not(:disabled):not([data-disabled]) {
165
+ text-decoration: underline;
166
+ text-underline-offset: 2px;
167
+ }
168
+
169
+ /* =============================================================================
170
+ Micro-Animations
171
+ ============================================================================= */
172
+
173
+ /* Hover lift — subtle elevation on hover (not on link or ghost style) */
174
+ [data-button]:not([data-style='link']):not([data-style='ghost']):hover:not(:disabled):not(
175
+ [data-disabled]
176
+ ) {
177
+ transform: translateY(-1px);
178
+ }
179
+
180
+ /* Press feedback — scale down on active */
181
+ [data-button]:active:not(:disabled):not([data-disabled]) {
182
+ transform: scale(0.97) translateY(0);
183
+ }
184
+
185
+ /* Icon shift — trailing icon moves right on hover */
186
+ [data-button]:hover:not(:disabled):not([data-disabled]) [data-button-icon-right] {
187
+ translate: 0.125rem 0;
188
+ }
189
+
190
+ [data-button] [data-button-icon-right] {
191
+ transition: translate 150ms ease;
192
+ }
193
+
194
+ /* Loading pulse */
195
+ [data-button][data-loading] {
196
+ animation: button-loading-pulse 1.5s ease-in-out infinite;
197
+ }
198
+
199
+ @keyframes button-loading-pulse {
200
+ 0%,
201
+ 100% {
202
+ opacity: 1;
203
+ }
204
+ 50% {
205
+ opacity: 0.7;
206
+ }
207
+ }
208
+
209
+ /* =============================================================================
210
+ Loading Spinner
211
+ ============================================================================= */
212
+
213
+ [data-button-spinner] {
214
+ width: 1em;
215
+ height: 1em;
216
+ border: 2px solid currentColor;
217
+ border-right-color: transparent;
218
+ border-radius: 50%;
219
+ animation: button-spin 0.6s linear infinite;
220
+ flex-shrink: 0;
221
+ }
222
+
223
+ @keyframes button-spin {
224
+ to {
225
+ transform: rotate(360deg);
226
+ }
227
+ }
228
+
229
+ /* =============================================================================
230
+ Button Group
231
+ ============================================================================= */
232
+
233
+ [data-button-group] {
234
+ display: inline-flex;
235
+ align-items: center;
236
+ }
237
+
238
+ [data-button-group] [data-button] {
239
+ border-radius: 0;
240
+ }
241
+
242
+ [data-button-group] [data-button]:first-child {
243
+ border-top-left-radius: 0.5rem;
244
+ border-bottom-left-radius: 0.5rem;
245
+ }
246
+
247
+ [data-button-group] [data-button]:last-child {
248
+ border-top-right-radius: 0.5rem;
249
+ border-bottom-right-radius: 0.5rem;
250
+ }
251
+
252
+ [data-button-group] [data-button]:not(:first-child) {
253
+ margin-left: -1px;
254
+ }
@@ -0,0 +1,39 @@
1
+ /* Card — base structural styles */
2
+
3
+ [data-card] {
4
+ display: flex;
5
+ flex-direction: column;
6
+ border-radius: var(--density-radius-base);
7
+ overflow: hidden;
8
+ text-decoration: none;
9
+ color: inherit;
10
+ }
11
+
12
+ button[data-card] {
13
+ background: none;
14
+ border: none;
15
+ cursor: pointer;
16
+ text-align: inherit;
17
+ font: inherit;
18
+ padding: 0;
19
+ }
20
+
21
+ a[data-card] {
22
+ cursor: pointer;
23
+ }
24
+
25
+ [data-card-header] {
26
+ padding-block: var(--density-spacing-md) var(--density-spacing-sm);
27
+ padding-inline: var(--density-spacing-lg);
28
+ }
29
+
30
+ [data-card-body] {
31
+ padding-block: var(--density-spacing-sm);
32
+ padding-inline: var(--density-spacing-lg);
33
+ flex: 1;
34
+ }
35
+
36
+ [data-card-footer] {
37
+ padding-block: var(--density-spacing-sm) var(--density-spacing-md);
38
+ padding-inline: var(--density-spacing-lg);
39
+ }