@rokkit/themes 1.0.0-next.14 → 1.0.0-next.141
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +106 -9
- package/build.mjs +235 -0
- package/dist/base.css +4934 -0
- package/dist/glass.css +1400 -0
- package/dist/index.css +10991 -0
- package/dist/material.css +1435 -0
- package/dist/minimal.css +1389 -0
- package/dist/rokkit.css +1829 -0
- package/package.json +53 -52
- package/src/base/breadcrumbs.css +47 -0
- package/src/base/button.css +254 -0
- package/src/base/card.css +36 -0
- package/src/base/carousel.css +128 -0
- package/src/base/connector.css +92 -0
- package/src/base/display.css +91 -0
- package/src/base/floating-action.css +388 -0
- package/src/base/floating-navigation.css +405 -0
- package/src/base/graph-paper.css +83 -0
- package/src/base/grid.css +93 -0
- package/src/base/index.css +40 -0
- package/src/base/input.css +238 -0
- package/src/base/item.css +78 -0
- package/src/base/list.css +179 -0
- package/src/base/menu.css +274 -0
- package/src/base/pill.css +57 -0
- package/src/base/progress.css +34 -0
- package/src/base/range.css +121 -0
- package/src/base/rating.css +42 -0
- package/src/base/reveal.css +37 -0
- package/src/base/search-filter.css +132 -0
- package/src/base/select.css +411 -0
- package/src/base/shine.css +14 -0
- package/src/base/stepper.css +140 -0
- package/src/base/switch.css +152 -0
- package/src/base/table.css +159 -0
- package/src/base/tabs.css +171 -0
- package/src/base/tilt.css +14 -0
- package/src/base/timeline.css +84 -0
- package/src/base/toggle.css +138 -0
- package/src/base/toolbar.css +337 -0
- package/src/base/tree.css +235 -0
- package/src/base/upload-progress.css +155 -0
- package/src/base/upload-target.css +67 -0
- package/src/glass/button.css +153 -0
- package/src/glass/card.css +66 -0
- package/src/glass/floating-action.css +61 -0
- package/src/glass/floating-navigation.css +74 -0
- package/src/glass/index.css +23 -0
- package/src/glass/input.css +126 -0
- package/src/glass/list.css +122 -0
- package/src/glass/menu.css +92 -0
- package/src/glass/range.css +61 -0
- package/src/glass/search-filter.css +49 -0
- package/src/glass/select.css +178 -0
- package/src/glass/switch.css +28 -0
- package/src/glass/table.css +87 -0
- package/src/glass/tabs.css +58 -0
- package/src/glass/timeline.css +46 -0
- package/src/glass/toggle.css +48 -0
- package/src/glass/toolbar.css +84 -0
- package/src/glass/tree.css +110 -0
- package/src/index.css +18 -0
- package/src/index.js +25 -2
- package/src/material/button.css +157 -0
- package/src/material/card.css +61 -0
- package/src/material/floating-action.css +60 -0
- package/src/material/floating-navigation.css +74 -0
- package/src/material/index.css +23 -0
- package/src/material/input.css +159 -0
- package/src/material/list.css +126 -0
- package/src/material/menu.css +92 -0
- package/src/material/range.css +62 -0
- package/src/material/search-filter.css +49 -0
- package/src/material/select.css +170 -0
- package/src/material/switch.css +28 -0
- package/src/material/table.css +91 -0
- package/src/material/tabs.css +62 -0
- package/src/material/timeline.css +46 -0
- package/src/material/toggle.css +48 -0
- package/src/material/toolbar.css +85 -0
- package/src/material/tree.css +103 -0
- package/src/minimal/button.css +155 -0
- package/src/minimal/card.css +61 -0
- package/src/minimal/floating-action.css +59 -0
- package/src/minimal/floating-navigation.css +70 -0
- package/src/minimal/index.css +23 -0
- package/src/minimal/input.css +119 -0
- package/src/minimal/list.css +126 -0
- package/src/minimal/menu.css +88 -0
- package/src/minimal/range.css +61 -0
- package/src/minimal/search-filter.css +49 -0
- package/src/minimal/select.css +170 -0
- package/src/minimal/switch.css +28 -0
- package/src/minimal/table.css +89 -0
- package/src/minimal/tabs.css +68 -0
- package/src/minimal/timeline.css +45 -0
- package/src/minimal/toggle.css +48 -0
- package/src/minimal/toolbar.css +85 -0
- package/src/minimal/tree.css +106 -0
- package/src/palette.css +7 -0
- package/src/rokkit/button.css +254 -0
- package/src/rokkit/card.css +64 -0
- package/src/rokkit/connector.css +11 -0
- package/src/rokkit/floating-action.css +65 -0
- package/src/rokkit/floating-navigation.css +83 -0
- package/src/rokkit/grid.css +46 -0
- package/src/rokkit/index.css +27 -0
- package/src/rokkit/input.css +134 -0
- package/src/rokkit/list.css +132 -0
- package/src/rokkit/menu.css +93 -0
- package/src/rokkit/range.css +62 -0
- package/src/rokkit/search-filter.css +49 -0
- package/src/rokkit/select.css +190 -0
- package/src/rokkit/switch.css +28 -0
- package/src/rokkit/table.css +89 -0
- package/src/rokkit/tabs.css +87 -0
- package/src/rokkit/timeline.css +46 -0
- package/src/rokkit/toggle.css +48 -0
- package/src/rokkit/toolbar.css +84 -0
- package/src/rokkit/tree.css +149 -0
- package/src/rokkit/upload-progress.css +102 -0
- package/src/rokkit/upload-target.css +50 -0
- package/src/constants.js +0 -4
- package/src/utils.js +0 -88
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,13 +1,110 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @rokkit/themes
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Pre-built CSS themes for `@rokkit/ui` components.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
- Modern
|
|
7
|
-
- Material
|
|
5
|
+
## Install
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
```sh
|
|
8
|
+
npm install @rokkit/themes
|
|
9
|
+
# or
|
|
10
|
+
bun add @rokkit/themes
|
|
11
|
+
```
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
## Overview
|
|
14
|
+
|
|
15
|
+
`@rokkit/themes` provides visual styles for `@rokkit/ui` components. Components in `@rokkit/ui` are unstyled by default — themes layer on top without requiring changes to component markup. Styles target semantic `data-*` attributes that components emit (e.g. `[data-button]`, `[data-list-item]`).
|
|
16
|
+
|
|
17
|
+
Available themes:
|
|
18
|
+
|
|
19
|
+
| Theme | Description |
|
|
20
|
+
| ---------- | -------------------------------------------------------------------- |
|
|
21
|
+
| `rokkit` | Default — gradients and glowing borders |
|
|
22
|
+
| `minimal` | Clean and subtle |
|
|
23
|
+
| `material` | Elevation and shadows |
|
|
24
|
+
| `glass` | Blur and transparency |
|
|
25
|
+
| `base` | Structural styles only (layout and positioning, no visual treatment) |
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
### Full bundle (base + all themes)
|
|
30
|
+
|
|
31
|
+
```css
|
|
32
|
+
@import '@rokkit/themes';
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Or in JavaScript/TypeScript:
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
import '@rokkit/themes'
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Single theme
|
|
42
|
+
|
|
43
|
+
```css
|
|
44
|
+
@import '@rokkit/themes/rokkit.css';
|
|
45
|
+
/* or */
|
|
46
|
+
@import '@rokkit/themes/minimal.css';
|
|
47
|
+
/* or */
|
|
48
|
+
@import '@rokkit/themes/material.css';
|
|
49
|
+
/* or */
|
|
50
|
+
@import '@rokkit/themes/glass.css';
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Base structural styles only
|
|
54
|
+
|
|
55
|
+
```css
|
|
56
|
+
@import '@rokkit/themes/base';
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Useful when writing a fully custom theme — imports layout and positioning rules without any visual styling.
|
|
60
|
+
|
|
61
|
+
### Individual component styles
|
|
62
|
+
|
|
63
|
+
```css
|
|
64
|
+
@import '@rokkit/themes/base/button.css';
|
|
65
|
+
@import '@rokkit/themes/rokkit/button.css';
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Theme scoping
|
|
69
|
+
|
|
70
|
+
The `rokkit` theme is the default and applies without any wrapper element.
|
|
71
|
+
|
|
72
|
+
Other themes are scoped using the `data-style` attribute:
|
|
73
|
+
|
|
74
|
+
```html
|
|
75
|
+
<!-- Apply minimal theme to a section -->
|
|
76
|
+
<div data-style="minimal">
|
|
77
|
+
<!-- @rokkit/ui components here use the minimal theme -->
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<!-- Apply a theme globally -->
|
|
81
|
+
<html data-style="material"></html>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Switching themes at runtime is a matter of updating the `data-style` attribute.
|
|
85
|
+
|
|
86
|
+
## Custom themes
|
|
87
|
+
|
|
88
|
+
To build your own theme:
|
|
89
|
+
|
|
90
|
+
1. Import `@rokkit/themes/base` for structural styles.
|
|
91
|
+
2. Write CSS targeting the same `data-*` attribute selectors used by the built-in themes.
|
|
92
|
+
3. Scope your selectors with `[data-style='my-theme']` to enable runtime switching.
|
|
93
|
+
|
|
94
|
+
Component attribute hooks follow the pattern `[data-style='my-theme'] [data-button]`, `[data-style='my-theme'] [data-list-item]`, etc.
|
|
95
|
+
|
|
96
|
+
## Architecture
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
src/
|
|
100
|
+
base/ -- Structural styles (layout, spacing, positioning)
|
|
101
|
+
rokkit/ -- Default theme (gradients + borders)
|
|
102
|
+
minimal/ -- Clean + subtle theme
|
|
103
|
+
material/ -- Elevation + shadows theme
|
|
104
|
+
glass/ -- Blur + transparency theme
|
|
105
|
+
index.css -- Full bundle entry point
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
Part of [Rokkit](https://github.com/jerrythomas/rokkit) — a Svelte 5 component library and design system.
|
package/build.mjs
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
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) => readFileSync(join(distDir, `${name}.css`), 'utf-8'))
|
|
226
|
+
writeFileSync(join(distDir, 'index.css'), bundleParts.join('\n'), 'utf-8')
|
|
227
|
+
console.log('✓ dist/index.css (full bundle)')
|
|
228
|
+
|
|
229
|
+
console.log('\n@rokkit/themes build complete.')
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
build().catch((err) => {
|
|
233
|
+
console.error('Build failed:', err)
|
|
234
|
+
process.exit(1)
|
|
235
|
+
})
|