strata-css 1.1.0 → 1.2.7
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/CHANGELOG.md +158 -62
- package/README.md +11 -9
- package/bin/strata.js +15 -11
- package/package.json +1 -1
- package/src/components/modules/chart/src/chart.ts +55 -21
- package/src/layers/base.js +4 -0
- package/src/registry/registry.js +349 -44
package/CHANGELOG.md
CHANGED
|
@@ -1,62 +1,158 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to Strata CSS will be documented here.
|
|
4
|
-
|
|
5
|
-
## [1.
|
|
6
|
-
|
|
7
|
-
###
|
|
8
|
-
- `
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
-
|
|
44
|
-
- `
|
|
45
|
-
- `
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
-
|
|
60
|
-
-
|
|
61
|
-
|
|
62
|
-
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to Strata CSS will be documented here.
|
|
4
|
+
|
|
5
|
+
## [1.2.7] — 2026-06-15
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- **Components bundle (`dist/strata.components.js`) now creates the `Strata` namespace.** Each component's UMD wrapper attaches to `Strata.*` only `if (root.Strata)` exists, but the bundle never initialised it — so every component fell back to its own global (`StrataChart`, `StrataModal`, …) and `window.Strata` was never defined, breaking all `Strata.Chart.create(...)` / `Strata.Modal.open(...)` usage (e.g. blank charts in `examples/chart.html`). The bundle banner (`bin/strata.js`) now initialises `Strata` before the wrappers run.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## [1.2.6] — 2026-06-05
|
|
13
|
+
|
|
14
|
+
### Docs
|
|
15
|
+
- Versioning rules clarified and expanded in `CONTRIBUTING.md`
|
|
16
|
+
- All package `CLAUDE.md` files updated with complete API references
|
|
17
|
+
- `README.md` files added to `@strata-packages/picker` and `@strata-packages/forms`
|
|
18
|
+
- `CHANGELOG.md` files added to `@strata-packages/picker` and `@strata-packages/forms`
|
|
19
|
+
- Root `README.md` updated: version badge, forms/picker added to standalone packages table
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## [1.2.5] — 2026-06-04
|
|
24
|
+
|
|
25
|
+
### Added — `@strata-packages/picker`
|
|
26
|
+
- `theme` option — per-instance inline CSS variable overrides (primary, bg, text, radius, shadow, cellSize, fontSize)
|
|
27
|
+
- `className` option — extra class on popup for targeted CSS overrides
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## [1.2.4] — 2026-06-03
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
- `@strata-packages/picker` — new standalone package: date, time, and datetime picker
|
|
35
|
+
- Zero dependencies, works standalone or as `Strata.Picker` with Strata CSS
|
|
36
|
+
- Declarative init via `data-st-datepicker`, `data-st-timepicker`, `data-st-datetimepicker`
|
|
37
|
+
- Date range selection with two-input mode and range highlight
|
|
38
|
+
- Preset shortcuts (built-in and custom)
|
|
39
|
+
- Month/year grid navigation
|
|
40
|
+
- `--stp-*` CSS variable system; auto-inherits `--st-*` tokens when Strata CSS is present
|
|
41
|
+
|
|
42
|
+
### Fixed
|
|
43
|
+
- Picker popup now appends to `<body>` with no CSS opacity transition — appears immediately on open
|
|
44
|
+
- `position: fixed` popup no longer adds scroll offset to viewport coordinates
|
|
45
|
+
- Picker rewritten as unified `createPicker` — date / time / datetime all working correctly
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## [1.2.3] — 2026-06-02
|
|
50
|
+
|
|
51
|
+
### Fixed
|
|
52
|
+
- `@strata-packages/forms` auto-init now recognises all `data-st-*` select attributes at DOMContentLoaded
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## [1.2.2] — 2026-06-01
|
|
57
|
+
|
|
58
|
+
### Added — `@strata-packages/forms`
|
|
59
|
+
- Checkbox select mode: dropdown stays open while ticking, Select All row, group-level checkboxes, `checkboxDisplay`: `chips` / `count` / `list`
|
|
60
|
+
- `maxDisplay` — fixed-height chip trigger with `+N` overflow badge
|
|
61
|
+
- Search input always rendered inside the dropdown (not above it)
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## [1.2.1] — 2026-05-30
|
|
66
|
+
|
|
67
|
+
### Fixed
|
|
68
|
+
- `@strata-packages/forms` backend-friendly `required` validation — triggers visible error state on custom trigger
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## [1.2.0] — 2026-05-28
|
|
73
|
+
|
|
74
|
+
### Added — `@strata-packages/forms`
|
|
75
|
+
- New standalone package: fully accessible custom select replacement
|
|
76
|
+
- Multi-select with chips, `maxItems`, searchable, clearable, grouped `<optgroup>`, creatable, avatar/custom render, async `loadOptions`, auto-width with viewport edge detection
|
|
77
|
+
- Native `<select>` stays in DOM — form submission works with any backend
|
|
78
|
+
- Declarative init via `data-st-select` and `data-st-*` option attributes
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## [1.1.0] — 2026-05-20
|
|
83
|
+
|
|
84
|
+
### Added
|
|
85
|
+
- Transition CSS variables: `--st-duration-theme`, `--st-easing-theme` — all hardcoded transition values replaced
|
|
86
|
+
- Sizing utilities: `max-w-{xs/sm/md/lg/xl/xxl/full/none}`, `min-w-{0/full/screen}`, `max-h-{full/screen/none}`, `min-h-{0/full/screen}`
|
|
87
|
+
- Arbitrary sizing: `max-w-[440px]`, `min-h-[300px]`, `max-h-[500px]`, `min-w-[200px]`
|
|
88
|
+
- Responsive variants added to 15 utility groups: `flex-{bp}`, `fw-{bp}`, `fst-{bp}`, `text-{bp}-{transform}`, `rounded-{bp}`, `shadow-{bp}`, `w-{bp}`, `h-{bp}`, `opacity-{bp}`, `overflow-{bp}`, `position-{bp}`, `cursor-{bp}`, `lh-{bp}`, `visible-{bp}`, `invisible-{bp}`
|
|
89
|
+
- Component CSS variable tokens: all hardcoded color values replaced with local CSS variables on `.badge`, `.btn-*`, `.btn-outline-*`, `.nav-pills .active`, `.list-group-item.active`, `.page-item.active`, `.dropdown-item.active`, `.progress-bar`, `.tooltip-inner`, `.navbar-dark`, `.card-img-overlay`, `.carousel-*`, `.table-dark`
|
|
90
|
+
- List utilities: `list-unstyled`, `list-inline`, `list-inline-item`, `list-disc`, `list-decimal`, `list-circle`, `list-square`, `list-none`, `list-lower-alpha`, `list-upper-alpha`, `list-lower-roman`, `list-upper-roman`, `list-inside`, `list-outside`, `list-spaced`
|
|
91
|
+
- Outline utilities: `outline-none`, `outline-{color}`, `outline-{1-5}`
|
|
92
|
+
- Label component: `.label` and `.label-{color}` aliases to `.badge` / `.badge-{color}` for Bootstrap 3 compatibility
|
|
93
|
+
|
|
94
|
+
### Fixed
|
|
95
|
+
- `text-[15px]` → `font-size: 15px` (length unit correctly detected)
|
|
96
|
+
- `text-[#f00]` → `color: #f00` (color value correctly detected)
|
|
97
|
+
- `#`, `(`, `)`, `,` in arbitrary values now correctly escaped in CSS class selectors
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## [1.0.0] — 2026-05-10
|
|
102
|
+
|
|
103
|
+
### Components
|
|
104
|
+
- `btn-primary`, `btn-secondary`, `btn-success`, `btn-danger`, `btn-warning`, `btn-info`, `btn-light`, `btn-dark` — full semantic button set with hover, focus, and active states baked in
|
|
105
|
+
- `card`, `card-header`, `card-body`, `card-footer` — composable card component
|
|
106
|
+
- `container`, `row`, `col-*` — Bootstrap-compatible responsive grid across all six breakpoints
|
|
107
|
+
- `modal` — dialog component with `data-st-toggle`, `data-st-dismiss`, and `data-st-backdrop` attribute API
|
|
108
|
+
- `navbar`, `navbar-brand`, `navbar-nav` — navigation bar component
|
|
109
|
+
- Skeleton loader — animated loading placeholder with `Strata.skeleton` JavaScript API
|
|
110
|
+
|
|
111
|
+
### Utilities
|
|
112
|
+
- Spacing: `mt-*`, `mb-*`, `ms-*`, `me-*`, `pt-*`, `pb-*`, `px-*`, `py-*`, `mx-auto`, `my-*`
|
|
113
|
+
- Display: `d-flex`, `d-none`, `d-block`, `d-grid`, `d-inline`, `d-inline-flex`, `d-inline-block`
|
|
114
|
+
- Colors: `text-*`, `bg-*` — all semantic colors (primary, secondary, success, danger, warning, info, light, dark, muted)
|
|
115
|
+
- Sizing: `w-25`, `w-50`, `w-75`, `w-100`, `h-25`, `h-50`, `h-75`, `h-100`, `mw-100`, `mh-100`
|
|
116
|
+
- Flexbox: `justify-content-*`, `align-items-*`, `align-self-*`, `flex-wrap`, `flex-nowrap`, `flex-grow-*`, `flex-shrink-*`
|
|
117
|
+
- Position: `position-static`, `position-relative`, `position-absolute`, `position-fixed`, `position-sticky`
|
|
118
|
+
- Overflow: `overflow-auto`, `overflow-hidden`, `overflow-scroll`, `overflow-visible`
|
|
119
|
+
- Opacity: `opacity-0`, `opacity-25`, `opacity-50`, `opacity-75`, `opacity-100`
|
|
120
|
+
- Visibility: `visible`, `invisible`
|
|
121
|
+
- Z-index: `z-0` through `z-3`
|
|
122
|
+
- Cursor: `cursor-pointer`, `cursor-default`, `cursor-not-allowed`, `cursor-wait`
|
|
123
|
+
- Shadows: `shadow-none`, `shadow-sm`, `shadow`, `shadow-lg`
|
|
124
|
+
- Transitions: `transition`, `transition-fast`, `transition-slow`, `transition-none`
|
|
125
|
+
- Easing: `ease-in`, `ease-out`, `ease-in-out`, `ease-linear`
|
|
126
|
+
- Arbitrary values: `mt-[24px]`, `bg-[#ff0000]`, `w-[347px]`, `transition-[background-color_0.3s_ease]`
|
|
127
|
+
- Important variants: `!mt-0`, `!d-none`, `!p-0`
|
|
128
|
+
- Breakpoint variants on all utilities: `col-md-6`, `d-lg-none`, `mt-xl-4`, `px-xxl-5`
|
|
129
|
+
|
|
130
|
+
### Theming
|
|
131
|
+
- Three built-in themes: `light` (default), `dark`, `dim` — applied via `data-st-theme` on `<html>`
|
|
132
|
+
- Automatic system preference detection via `prefers-color-scheme` — no configuration needed
|
|
133
|
+
- Unlimited custom themes via CSS custom properties: `[data-st-theme="brand"] { --st-primary: #7c3aed }`
|
|
134
|
+
- All `--st-*` custom properties fully overridable in `:root` or any selector
|
|
135
|
+
- Smooth theme transitions — all elements animate when the theme attribute changes
|
|
136
|
+
|
|
137
|
+
### State Management
|
|
138
|
+
- `data-st-visible="true|false"` — fade + translateY transition for show/hide
|
|
139
|
+
- `data-st-collapsed="true|false"` — smooth `max-height` expand/collapse
|
|
140
|
+
- `data-st-loading="true|false"` — opacity reduction + pointer-events disabled
|
|
141
|
+
- `data-st-disabled="true|false"` — opacity reduction + `cursor: not-allowed`
|
|
142
|
+
- `data-st-theme="light|dark|dim|custom"` — live theme switching
|
|
143
|
+
|
|
144
|
+
### Build System
|
|
145
|
+
- PostCSS plugin with O(1) class registry — 1065 pre-computed entries, zero linear scanning
|
|
146
|
+
- Multi-layer caching: dirty flag, file mtime, glob hash, config hash, output string cache
|
|
147
|
+
- CSS `@layer` hierarchy: `st-base` → `st-components` → `st-utilities` — breakpoint order guaranteed, HTML class order irrelevant
|
|
148
|
+
- Bootstrap-style breakpoints: xs (0px), sm (576px), md (768px), lg (992px), xl (1200px), xxl (1400px)
|
|
149
|
+
- Custom breakpoints via `strata.config.js` `theme.breakpoints`
|
|
150
|
+
- `prefers-reduced-motion` respected automatically — no configuration needed
|
|
151
|
+
- CLI: `strata init` (scaffold), `strata --watch` (development), `strata --build` (production), `strata --minify` (minified production)
|
|
152
|
+
|
|
153
|
+
### Performance (vs Tailwind CSS 3 in watch mode)
|
|
154
|
+
- Cold build: 1.89ms avg vs 7.21ms — 3.8× faster
|
|
155
|
+
- Warm rebuild: 0.14ms avg vs 2.70ms — 19× faster
|
|
156
|
+
- Warm p95: 0.23ms vs 6.12ms — 26× faster
|
|
157
|
+
|
|
158
|
+
Reproduce via `npm run benchmark`.
|
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
**A modern CSS framework combining Bootstrap's component architecture with Tailwind's JIT processing.**
|
|
6
6
|
|
|
7
7
|
[](LICENSE)
|
|
8
|
-
[]()
|
|
9
9
|
[]()
|
|
10
10
|
[](https://www.npmjs.com/package/strata-css)
|
|
11
11
|
[]()
|
|
@@ -383,9 +383,11 @@ All Strata plugins are available as independent packages. Use them without Strat
|
|
|
383
383
|
|
|
384
384
|
| Package | Standalone global | With Strata | Install |
|
|
385
385
|
|---|---|---|---|
|
|
386
|
-
| `@strata-
|
|
387
|
-
| `@strata-
|
|
388
|
-
| `@strata-
|
|
386
|
+
| `@strata-packages/forms` | `StrataForms` | `Strata.Forms` | `npm i @strata-packages/forms` |
|
|
387
|
+
| `@strata-packages/picker` | `StrataPicker` | `Strata.Picker` | `npm i @strata-packages/picker` |
|
|
388
|
+
| `@strata-packages/skeleton-loader` | `SkeletonLoader` | `Strata.skeleton` | `npm i @strata-packages/skeleton-loader` |
|
|
389
|
+
| `@strata-packages/modal` | `StrataModal` | `Strata.Modal` | `npm i @strata-packages/modal` |
|
|
390
|
+
| `@strata-packages/chart` | `StrataChart` | `Strata.Chart` | `npm i @strata-packages/chart` |
|
|
389
391
|
|
|
390
392
|
### How detection works
|
|
391
393
|
|
|
@@ -395,18 +397,18 @@ When `strata.components.js` is loaded it sets `data-strata` on `<html>`. Each pl
|
|
|
395
397
|
|
|
396
398
|
```html
|
|
397
399
|
<!-- Skeleton -->
|
|
398
|
-
<link rel="stylesheet" href="node_modules/@strata-
|
|
399
|
-
<script src="node_modules/@strata-
|
|
400
|
+
<link rel="stylesheet" href="node_modules/@strata-packages/skeleton-loader/skeleton-loader.css">
|
|
401
|
+
<script src="node_modules/@strata-packages/skeleton-loader/skeleton-loader.js"></script>
|
|
400
402
|
<script>SkeletonLoader.init('.card')</script>
|
|
401
403
|
|
|
402
404
|
<!-- Modal -->
|
|
403
|
-
<link rel="stylesheet" href="node_modules/@strata-
|
|
404
|
-
<script src="node_modules/@strata-
|
|
405
|
+
<link rel="stylesheet" href="node_modules/@strata-packages/modal/modal.css">
|
|
406
|
+
<script src="node_modules/@strata-packages/modal/modal.js"></script>
|
|
405
407
|
<script>StrataModal.open('#myModal')</script>
|
|
406
408
|
|
|
407
409
|
<!-- Chart (requires Three.js) -->
|
|
408
410
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
|
409
|
-
<script src="node_modules/@strata-
|
|
411
|
+
<script src="node_modules/@strata-packages/chart/chart.js"></script>
|
|
410
412
|
<script>StrataChart.create('#myChart', { type: 'bar', data: [...] })</script>
|
|
411
413
|
```
|
|
412
414
|
|
package/bin/strata.js
CHANGED
|
@@ -90,7 +90,11 @@ async function build(cssMinify = false, jsMinify = true) {
|
|
|
90
90
|
const jsDest = path.join(path.dirname(outputFile), 'strata.components.js')
|
|
91
91
|
if (fs.existsSync(componentsDir)) {
|
|
92
92
|
const files = fs.readdirSync(componentsDir).filter(f => f.endsWith('.js')).sort()
|
|
93
|
+
// Create the shared Strata namespace BEFORE the package UMD wrappers run, so
|
|
94
|
+
// each one attaches to Strata.* (their `if (root.Strata)` branch) instead of
|
|
95
|
+
// falling back to a separate StrataModal/StrataChart/etc. global.
|
|
93
96
|
const banner = `/*! Strata Components — built ${new Date().toISOString().slice(0,10)} */\n`
|
|
97
|
+
+ `;(function(g){g.Strata=g.Strata||{}})(typeof globalThis!=='undefined'?globalThis:this);\n`
|
|
94
98
|
const parts = files.map(f => fs.readFileSync(path.join(componentsDir, f), 'utf8'))
|
|
95
99
|
packageFiles.forEach(p => { if (fs.existsSync(p)) parts.push(fs.readFileSync(p, 'utf8')) })
|
|
96
100
|
const raw = parts.join('\n')
|
|
@@ -178,9 +182,9 @@ function detectFramework(cwd) {
|
|
|
178
182
|
// ─── Init helpers ─────────────────────────────────────────────────────
|
|
179
183
|
|
|
180
184
|
const PACKAGES = [
|
|
181
|
-
{ name: '@strata-
|
|
182
|
-
{ name: '@strata-
|
|
183
|
-
{ name: '@strata-
|
|
185
|
+
{ name: '@strata-packages/modal', label: 'modal — attribute-driven modal dialogs' },
|
|
186
|
+
{ name: '@strata-packages/skeleton-loader',label: 'skeleton-loader — shimmer loading placeholders' },
|
|
187
|
+
{ name: '@strata-packages/chart', label: 'chart — Three.js data visualisations' },
|
|
184
188
|
]
|
|
185
189
|
|
|
186
190
|
const FRAMEWORK_DEV = {
|
|
@@ -254,10 +258,10 @@ async function askCheckbox(rl, items) {
|
|
|
254
258
|
}
|
|
255
259
|
|
|
256
260
|
function packageUsageSnippet(pkgName) {
|
|
257
|
-
if (pkgName === '@strata-
|
|
261
|
+
if (pkgName === '@strata-packages/modal') return `
|
|
258
262
|
<!-- Modal usage (standalone) -->
|
|
259
|
-
<link rel="stylesheet" href="node_modules/@strata-
|
|
260
|
-
<script src="node_modules/@strata-
|
|
263
|
+
<link rel="stylesheet" href="node_modules/@strata-packages/modal/modal.css">
|
|
264
|
+
<script src="node_modules/@strata-packages/modal/modal.js"></script>
|
|
261
265
|
|
|
262
266
|
<button data-st-toggle="modal" data-st-target="#myModal">Open</button>
|
|
263
267
|
<div class="modal" id="myModal" aria-hidden="true">
|
|
@@ -270,10 +274,10 @@ function packageUsageSnippet(pkgName) {
|
|
|
270
274
|
</div></div>
|
|
271
275
|
</div>`
|
|
272
276
|
|
|
273
|
-
if (pkgName === '@strata-
|
|
277
|
+
if (pkgName === '@strata-packages/skeleton-loader') return `
|
|
274
278
|
<!-- Skeleton loader usage (standalone) -->
|
|
275
|
-
<link rel="stylesheet" href="node_modules/@strata-
|
|
276
|
-
<script src="node_modules/@strata-
|
|
279
|
+
<link rel="stylesheet" href="node_modules/@strata-packages/skeleton-loader/skeleton-loader.css">
|
|
280
|
+
<script src="node_modules/@strata-packages/skeleton-loader/skeleton-loader.js"></script>
|
|
277
281
|
|
|
278
282
|
<div class="card" data-st-skeleton="true">
|
|
279
283
|
<div class="card-body"><p>Content loading...</p></div>
|
|
@@ -283,10 +287,10 @@ function packageUsageSnippet(pkgName) {
|
|
|
283
287
|
fetchData().then(() => SkeletonLoader.reveal())
|
|
284
288
|
</script>`
|
|
285
289
|
|
|
286
|
-
if (pkgName === '@strata-
|
|
290
|
+
if (pkgName === '@strata-packages/chart') return `
|
|
287
291
|
<!-- Chart usage (standalone, requires Three.js) -->
|
|
288
292
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
|
289
|
-
<script src="node_modules/@strata-
|
|
293
|
+
<script src="node_modules/@strata-packages/chart/chart.js"></script>
|
|
290
294
|
|
|
291
295
|
<canvas id="myChart"></canvas>
|
|
292
296
|
<script>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "strata-css",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.7",
|
|
4
4
|
"_versioningNote": "Stable: 1.0.0 / 1.1.0 / 2.0.0 | Beta: 1.1.0-beta.1 / 1.1.0-beta.2",
|
|
5
5
|
"description": "A modern CSS framework combining Bootstrap components with Tailwind JIT processing",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -99,6 +99,10 @@ interface ChartOptions {
|
|
|
99
99
|
onReady?: (chart: StrataChart) => void
|
|
100
100
|
onChange?: (view: ChartView) => void
|
|
101
101
|
onClick?: (point: { label: string; value: number; category: string; index: number }) => void
|
|
102
|
+
|
|
103
|
+
// Three.js is lazy-loaded from this URL if window.THREE is absent. Set to ''
|
|
104
|
+
// to require the host to pre-load it.
|
|
105
|
+
threeUrl?: string
|
|
102
106
|
}
|
|
103
107
|
|
|
104
108
|
interface OrbitControlsInstance {
|
|
@@ -115,7 +119,10 @@ interface OrbitControlsInstance {
|
|
|
115
119
|
interface StrataNamespace { Chart: ChartPlugin }
|
|
116
120
|
|
|
117
121
|
interface ChartPlugin {
|
|
118
|
-
|
|
122
|
+
// Synchronous when window.THREE is present; otherwise lazy-loads Three.js and
|
|
123
|
+
// resolves to the instance (or null on failure).
|
|
124
|
+
create(selector: string | Element, options: ChartOptions): StrataChart | null | Promise<StrataChart | null>
|
|
125
|
+
load(url?: string): Promise<unknown>
|
|
119
126
|
destroyAll(): void
|
|
120
127
|
}
|
|
121
128
|
|
|
@@ -157,6 +164,8 @@ const CAMERA_2D = { x: 0, y: 2, z: 22, fov: 18 }
|
|
|
157
164
|
const DEFAULT_COLORS = ['#4a90e2', '#e25f4a', '#50c878', '#f5a623', '#9b59b6', '#1abc9c']
|
|
158
165
|
const VALID_TYPES = ['bar', 'line', 'pie', 'scatter'] as ChartType[]
|
|
159
166
|
const MAX_POINTS = 100_000
|
|
167
|
+
// Three.js is lazy-loaded on first chart creation if window.THREE is absent.
|
|
168
|
+
const DEFAULT_THREE_URL = 'https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.min.js'
|
|
160
169
|
const STRIP_HTML = /<[^>]*>/g
|
|
161
170
|
const SCALE_STEPS = 5
|
|
162
171
|
const GRID_COLOR_NORMAL = 0xd4d4d4
|
|
@@ -1097,29 +1106,54 @@ class StrataChart {
|
|
|
1097
1106
|
|
|
1098
1107
|
// ─── Bootstrap IIFE ───────────────────────────────────────────────────────────
|
|
1099
1108
|
|
|
1100
|
-
;(function (win: Window & typeof globalThis & { Strata?: Partial<StrataNamespace>; StrataChart?: Partial<StrataNamespace['Chart']> }) {
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1109
|
+
;(function (win: Window & typeof globalThis & { THREE?: unknown; Strata?: Partial<StrataNamespace>; StrataChart?: Partial<StrataNamespace['Chart']> }) {
|
|
1110
|
+
// Lazy Three.js loader — fetched only on first chart creation if absent.
|
|
1111
|
+
let threeCache: Promise<unknown> | null = null
|
|
1112
|
+
function loadThree(url?: string): Promise<unknown> {
|
|
1113
|
+
if (win.THREE) return Promise.resolve(win.THREE)
|
|
1114
|
+
if (!url) return Promise.reject(new Error('[Strata Chart] Three.js not found and no threeUrl configured.'))
|
|
1115
|
+
if (threeCache) return threeCache
|
|
1116
|
+
threeCache = new Promise((resolve, reject) => {
|
|
1117
|
+
const s = document.createElement('script')
|
|
1118
|
+
s.src = url; s.async = true
|
|
1119
|
+
s.onload = () => { if (win.THREE) resolve(win.THREE); else { threeCache = null; reject(new Error('[Strata Chart] Three.js did not register after loading ' + url)) } }
|
|
1120
|
+
s.onerror = () => { threeCache = null; reject(new Error('[Strata Chart] Failed to load ' + url)) }
|
|
1121
|
+
document.head.appendChild(s)
|
|
1122
|
+
})
|
|
1123
|
+
return threeCache
|
|
1104
1124
|
}
|
|
1105
1125
|
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1126
|
+
// Validate + construct. Assumes Three.js is present.
|
|
1127
|
+
function build(selector: string | Element, options: ChartOptions): StrataChart | null {
|
|
1128
|
+
const container = typeof selector === 'string' ? document.querySelector(selector) : selector as Element
|
|
1129
|
+
if (!container) { console.error(`[Strata Chart] Element not found: ${String(selector)}`); return null }
|
|
1130
|
+
if (registry.has(container)) {
|
|
1131
|
+
console.warn('[Strata Chart] Chart already mounted here. Call .destroy() first.')
|
|
1132
|
+
return registry.get(container)!
|
|
1133
|
+
}
|
|
1134
|
+
if (!Array.isArray(options?.data)) { console.error('[Strata Chart] options.data must be an array.'); return null }
|
|
1135
|
+
if (options.type && !VALID_TYPES.includes(options.type)) {
|
|
1136
|
+
console.error(`[Strata Chart] Invalid type "${options.type}". Use: ${VALID_TYPES.join(', ')}`)
|
|
1137
|
+
return null
|
|
1138
|
+
}
|
|
1139
|
+
const instance = new StrataChart(container as HTMLElement, options)
|
|
1140
|
+
registry.set(container, instance)
|
|
1141
|
+
return instance
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
const api: ChartPlugin = {
|
|
1145
|
+
// Synchronous + unchanged when Three.js is already present. If absent, it is
|
|
1146
|
+
// lazy-loaded and create() returns a Promise<instance> (override the source
|
|
1147
|
+
// with options.threeUrl, or '' to require pre-load).
|
|
1148
|
+
create(selector: string | Element, options: ChartOptions): StrataChart | null | Promise<StrataChart | null> {
|
|
1149
|
+
options = options || ({} as ChartOptions)
|
|
1150
|
+
if (win.THREE) return build(selector, options)
|
|
1151
|
+
const url = options.threeUrl === undefined ? DEFAULT_THREE_URL : options.threeUrl
|
|
1152
|
+
return loadThree(url)
|
|
1153
|
+
.then(() => build(selector, options))
|
|
1154
|
+
.catch((err: Error) => { console.error(err.message || err); return null })
|
|
1122
1155
|
},
|
|
1156
|
+
load(url?: string): Promise<unknown> { return loadThree(url === undefined ? DEFAULT_THREE_URL : url) },
|
|
1123
1157
|
destroyAll() { registry.forEach(inst => inst.destroy()) },
|
|
1124
1158
|
}
|
|
1125
1159
|
|
package/src/layers/base.js
CHANGED
package/src/registry/registry.js
CHANGED
|
@@ -18,6 +18,10 @@ function escapeClass(cls) {
|
|
|
18
18
|
.replace(/\//g, '\\/')
|
|
19
19
|
.replace(/:/g, '\\:')
|
|
20
20
|
.replace(/\./g, '\\.')
|
|
21
|
+
.replace(/#/g, '\\#')
|
|
22
|
+
.replace(/\(/g, '\\(')
|
|
23
|
+
.replace(/\)/g, '\\)')
|
|
24
|
+
.replace(/,/g, '\\,')
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
function parseArbitrary(value) {
|
|
@@ -510,12 +514,13 @@ reg('card-img', 'components', `.card-img {
|
|
|
510
514
|
}`)
|
|
511
515
|
|
|
512
516
|
reg('card-img-overlay', 'components', `.card-img-overlay {
|
|
517
|
+
--st-card-overlay-color: #fff;
|
|
513
518
|
position: absolute;
|
|
514
519
|
inset: 0;
|
|
515
520
|
padding: 1rem;
|
|
516
521
|
border-radius: calc(var(--st-border-radius) - 1px);
|
|
517
522
|
background: rgba(0, 0, 0, 0.45);
|
|
518
|
-
color:
|
|
523
|
+
color: var(--st-card-overlay-color);
|
|
519
524
|
}`)
|
|
520
525
|
|
|
521
526
|
reg('card-group', 'components', `.card-group {
|
|
@@ -598,12 +603,13 @@ reg('alert-link', 'components', `.alert-link {
|
|
|
598
603
|
// ─── Components — Badge ──────────────────────────────────────────────
|
|
599
604
|
|
|
600
605
|
reg('badge', 'components', `.badge {
|
|
606
|
+
--st-badge-color: #fff;
|
|
601
607
|
display: inline-block;
|
|
602
608
|
padding: 0.35em 0.65em;
|
|
603
609
|
font-size: 0.75em;
|
|
604
610
|
font-weight: 700;
|
|
605
611
|
line-height: 1;
|
|
606
|
-
color:
|
|
612
|
+
color: var(--st-badge-color);
|
|
607
613
|
text-align: center;
|
|
608
614
|
white-space: nowrap;
|
|
609
615
|
vertical-align: baseline;
|
|
@@ -613,23 +619,61 @@ reg('badge', 'components', `.badge {
|
|
|
613
619
|
|
|
614
620
|
const BADGE_COLORS = ['primary','secondary','success','danger','warning','info','light','dark']
|
|
615
621
|
BADGE_COLORS.forEach(color => {
|
|
616
|
-
const
|
|
622
|
+
const defaultFg = ['warning','info','light'].includes(color) ? 'var(--st-dark)' : '#fff'
|
|
617
623
|
reg(`badge-${color}`, 'components', `.badge-${color} {
|
|
624
|
+
--st-badge-color: ${defaultFg};
|
|
618
625
|
background-color: var(--st-${color});
|
|
619
|
-
color:
|
|
626
|
+
color: var(--st-badge-color);
|
|
620
627
|
}`)
|
|
621
628
|
})
|
|
622
629
|
|
|
623
630
|
reg('badge-pill', 'components', `.badge-pill { border-radius: 999px; }`)
|
|
624
631
|
reg('rounded-pill', 'components', `.rounded-pill { border-radius: 999px; }`)
|
|
625
632
|
|
|
633
|
+
// ─── Components — Label (Bootstrap 3 aliases) ────────────────────────
|
|
634
|
+
// .label and .label-{color} are Bootstrap 3's label component.
|
|
635
|
+
// Bootstrap 4+ renamed them to .badge / .badge-{color}.
|
|
636
|
+
// These are aliases — identical output to their badge equivalents so
|
|
637
|
+
// Bootstrap 3 markup works without changes.
|
|
638
|
+
|
|
639
|
+
reg('label', 'components', `.label {
|
|
640
|
+
--st-badge-color: #fff;
|
|
641
|
+
display: inline-block;
|
|
642
|
+
padding: 0.35em 0.65em;
|
|
643
|
+
font-size: 0.75em;
|
|
644
|
+
font-weight: 700;
|
|
645
|
+
line-height: 1;
|
|
646
|
+
color: var(--st-badge-color);
|
|
647
|
+
text-align: center;
|
|
648
|
+
white-space: nowrap;
|
|
649
|
+
vertical-align: baseline;
|
|
650
|
+
border-radius: var(--st-border-radius);
|
|
651
|
+
background-color: var(--st-secondary);
|
|
652
|
+
}`)
|
|
653
|
+
|
|
654
|
+
const LABEL_COLORS = ['default','primary','secondary','success','info','warning','danger','light','dark']
|
|
655
|
+
LABEL_COLORS.forEach(color => {
|
|
656
|
+
const mappedColor = color === 'default' ? 'secondary' : color
|
|
657
|
+
const defaultFg = ['warning','info','light'].includes(mappedColor) ? 'var(--st-dark)' : '#fff'
|
|
658
|
+
reg(`label-${color}`, 'components', `.label-${color} {
|
|
659
|
+
--st-badge-color: ${defaultFg};
|
|
660
|
+
background-color: var(--st-${mappedColor});
|
|
661
|
+
color: var(--st-badge-color);
|
|
662
|
+
}`)
|
|
663
|
+
})
|
|
664
|
+
|
|
626
665
|
// ─── Components — Buttons ────────────────────────────────────────────
|
|
627
666
|
|
|
628
667
|
const BTN_COLORS = ['primary','secondary','success','danger','warning','info','light','dark']
|
|
629
668
|
|
|
630
669
|
BTN_COLORS.forEach(color => {
|
|
631
|
-
const
|
|
670
|
+
const defaultFg = ['warning','info','light'].includes(color) ? 'var(--st-dark)' : '#fff'
|
|
632
671
|
reg(`btn-${color}`, 'components', `.btn-${color} {
|
|
672
|
+
--st-btn-color: ${defaultFg};
|
|
673
|
+
--st-btn-bg: var(--st-${color});
|
|
674
|
+
--st-btn-border: var(--st-${color});
|
|
675
|
+
--st-btn-hover-bg: var(--st-${color}-hover, color-mix(in srgb, var(--st-${color}) 85%, black));
|
|
676
|
+
--st-btn-hover-border:var(--st-${color}-hover, color-mix(in srgb, var(--st-${color}) 85%, black));
|
|
633
677
|
display: inline-flex;
|
|
634
678
|
align-items: center;
|
|
635
679
|
justify-content: center;
|
|
@@ -637,9 +681,9 @@ BTN_COLORS.forEach(color => {
|
|
|
637
681
|
font-size: 1rem;
|
|
638
682
|
font-weight: 400;
|
|
639
683
|
line-height: 1.5;
|
|
640
|
-
color:
|
|
641
|
-
background-color: var(--st
|
|
642
|
-
border: 1px solid var(--st
|
|
684
|
+
color: var(--st-btn-color);
|
|
685
|
+
background-color: var(--st-btn-bg);
|
|
686
|
+
border: 1px solid var(--st-btn-border);
|
|
643
687
|
border-radius: var(--st-border-radius);
|
|
644
688
|
cursor: pointer;
|
|
645
689
|
text-decoration: none;
|
|
@@ -653,9 +697,9 @@ BTN_COLORS.forEach(color => {
|
|
|
653
697
|
}
|
|
654
698
|
|
|
655
699
|
.btn-${color}:hover {
|
|
656
|
-
background-color: var(--st
|
|
657
|
-
border-color: var(--st
|
|
658
|
-
color:
|
|
700
|
+
background-color: var(--st-btn-hover-bg);
|
|
701
|
+
border-color: var(--st-btn-hover-border);
|
|
702
|
+
color: var(--st-btn-color);
|
|
659
703
|
}
|
|
660
704
|
|
|
661
705
|
.btn-${color}:focus-visible {
|
|
@@ -668,6 +712,9 @@ BTN_COLORS.forEach(color => {
|
|
|
668
712
|
}`)
|
|
669
713
|
|
|
670
714
|
reg(`btn-outline-${color}`, 'components', `.btn-outline-${color} {
|
|
715
|
+
--st-btn-outline-color: var(--st-${color});
|
|
716
|
+
--st-btn-outline-hover-color:${defaultFg};
|
|
717
|
+
--st-btn-outline-hover-bg: var(--st-${color});
|
|
671
718
|
display: inline-flex;
|
|
672
719
|
align-items: center;
|
|
673
720
|
justify-content: center;
|
|
@@ -675,7 +722,7 @@ BTN_COLORS.forEach(color => {
|
|
|
675
722
|
font-size: 1rem;
|
|
676
723
|
font-weight: 400;
|
|
677
724
|
line-height: 1.5;
|
|
678
|
-
color: var(--st
|
|
725
|
+
color: var(--st-btn-outline-color);
|
|
679
726
|
background-color: transparent;
|
|
680
727
|
border: 1px solid var(--st-${color});
|
|
681
728
|
border-radius: var(--st-border-radius);
|
|
@@ -689,8 +736,8 @@ BTN_COLORS.forEach(color => {
|
|
|
689
736
|
}
|
|
690
737
|
|
|
691
738
|
.btn-outline-${color}:hover {
|
|
692
|
-
background-color: var(--st
|
|
693
|
-
color:
|
|
739
|
+
background-color: var(--st-btn-outline-hover-bg);
|
|
740
|
+
color: var(--st-btn-outline-hover-color);
|
|
694
741
|
}
|
|
695
742
|
|
|
696
743
|
.btn-outline-${color}:focus-visible {
|
|
@@ -949,8 +996,9 @@ reg('nav-pills', 'components', `.nav-pills .nav-link {
|
|
|
949
996
|
}
|
|
950
997
|
|
|
951
998
|
.nav-pills .nav-link.active {
|
|
999
|
+
--st-nav-pills-active-color: #fff;
|
|
952
1000
|
background-color: var(--st-primary);
|
|
953
|
-
color:
|
|
1001
|
+
color: var(--st-nav-pills-active-color);
|
|
954
1002
|
}`)
|
|
955
1003
|
|
|
956
1004
|
reg('nav-fill', 'components', `.nav-fill .nav-item { flex: 1 1 auto; text-align: center; }`)
|
|
@@ -1333,10 +1381,11 @@ reg('list-group-item', 'components', `.list-group-item {
|
|
|
1333
1381
|
}
|
|
1334
1382
|
|
|
1335
1383
|
.list-group-item.active {
|
|
1384
|
+
--st-list-group-active-color: #fff;
|
|
1336
1385
|
z-index: 2;
|
|
1337
1386
|
background-color: var(--st-primary);
|
|
1338
1387
|
border-color: var(--st-primary);
|
|
1339
|
-
color:
|
|
1388
|
+
color: var(--st-list-group-active-color);
|
|
1340
1389
|
}
|
|
1341
1390
|
|
|
1342
1391
|
.list-group-item.disabled {
|
|
@@ -1410,11 +1459,12 @@ reg('progress', 'components', `.progress {
|
|
|
1410
1459
|
}`)
|
|
1411
1460
|
|
|
1412
1461
|
reg('progress-bar', 'components', `.progress-bar {
|
|
1462
|
+
--st-progress-bar-color: #fff;
|
|
1413
1463
|
display: flex;
|
|
1414
1464
|
flex-direction: column;
|
|
1415
1465
|
justify-content: center;
|
|
1416
1466
|
overflow: hidden;
|
|
1417
|
-
color:
|
|
1467
|
+
color: var(--st-progress-bar-color);
|
|
1418
1468
|
text-align: center;
|
|
1419
1469
|
white-space: nowrap;
|
|
1420
1470
|
background-color: var(--st-primary);
|
|
@@ -1531,9 +1581,10 @@ reg('page-item', 'components', `.page-item.disabled .page-link {
|
|
|
1531
1581
|
}
|
|
1532
1582
|
|
|
1533
1583
|
.page-item.active .page-link {
|
|
1584
|
+
--st-pagination-active-color: #fff;
|
|
1534
1585
|
background-color: var(--st-primary);
|
|
1535
1586
|
border-color: var(--st-primary);
|
|
1536
|
-
color:
|
|
1587
|
+
color: var(--st-pagination-active-color);
|
|
1537
1588
|
}`)
|
|
1538
1589
|
|
|
1539
1590
|
reg('page-link', 'components', `.page-link {
|
|
@@ -1926,8 +1977,9 @@ reg('dropdown-item', 'components', `.dropdown-item {
|
|
|
1926
1977
|
|
|
1927
1978
|
.dropdown-item.active,
|
|
1928
1979
|
.dropdown-item:active {
|
|
1980
|
+
--st-dropdown-active-color: #fff;
|
|
1929
1981
|
background-color: var(--st-primary);
|
|
1930
|
-
color:
|
|
1982
|
+
color: var(--st-dropdown-active-color);
|
|
1931
1983
|
}
|
|
1932
1984
|
|
|
1933
1985
|
.dropdown-item.disabled {
|
|
@@ -2103,12 +2155,17 @@ Object.entries(TABLE_COLORS).forEach(([color, bg]) => {
|
|
|
2103
2155
|
.table-${color} > :not(caption) > * > * { background-color: var(--st-table-bg); }`)
|
|
2104
2156
|
})
|
|
2105
2157
|
reg('table-dark', 'components', `.table-dark {
|
|
2106
|
-
--st-table-bg:
|
|
2107
|
-
color:
|
|
2108
|
-
border
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2158
|
+
--st-table-dark-bg: #212529;
|
|
2159
|
+
--st-table-dark-color: #dee2e6;
|
|
2160
|
+
--st-table-dark-border: #373b3e;
|
|
2161
|
+
--st-table-dark-head-bg: #1a1d20;
|
|
2162
|
+
--st-table-dark-head-color: #adb5bd;
|
|
2163
|
+
--st-table-bg: var(--st-table-dark-bg);
|
|
2164
|
+
color: var(--st-table-dark-color);
|
|
2165
|
+
border-color: var(--st-table-dark-border);
|
|
2166
|
+
}
|
|
2167
|
+
.table-dark > :not(caption) > * > * { background-color: var(--st-table-bg); color: var(--st-table-dark-color); }
|
|
2168
|
+
.table-dark > thead > tr > th { background-color: var(--st-table-dark-head-bg); color: var(--st-table-dark-head-color); }`)
|
|
2112
2169
|
reg('table-striped-columns', 'components', `.table-striped-columns > :not(caption) > tr > :nth-child(even) {
|
|
2113
2170
|
background-color: var(--st-bg-secondary);
|
|
2114
2171
|
}`)
|
|
@@ -2171,22 +2228,25 @@ reg('carousel-item', 'components', `.carousel-item {
|
|
|
2171
2228
|
.carousel-item-next,
|
|
2172
2229
|
.carousel-item-prev { display: block; }`)
|
|
2173
2230
|
reg('carousel-control-prev', 'components', `.carousel-control-prev {
|
|
2231
|
+
--st-carousel-control-color: #fff;
|
|
2174
2232
|
position: absolute; top: 0; bottom: 0; left: 0;
|
|
2175
2233
|
display: flex; align-items: center; justify-content: center;
|
|
2176
|
-
width: 15%; padding: 0; color:
|
|
2234
|
+
width: 15%; padding: 0; color: var(--st-carousel-control-color); text-align: center;
|
|
2177
2235
|
background: rgba(0,0,0,0.2); border: 0; opacity: 0.5;
|
|
2178
2236
|
cursor: pointer; transition: opacity var(--st-duration) var(--st-easing);
|
|
2179
2237
|
}
|
|
2180
2238
|
.carousel-control-prev:hover { opacity: 0.9; }`)
|
|
2181
2239
|
reg('carousel-control-next', 'components', `.carousel-control-next {
|
|
2240
|
+
--st-carousel-control-color: #fff;
|
|
2182
2241
|
position: absolute; top: 0; right: 0; bottom: 0;
|
|
2183
2242
|
display: flex; align-items: center; justify-content: center;
|
|
2184
|
-
width: 15%; padding: 0; color:
|
|
2243
|
+
width: 15%; padding: 0; color: var(--st-carousel-control-color); text-align: center;
|
|
2185
2244
|
background: rgba(0,0,0,0.2); border: 0; opacity: 0.5;
|
|
2186
2245
|
cursor: pointer; transition: opacity var(--st-duration) var(--st-easing);
|
|
2187
2246
|
}
|
|
2188
2247
|
.carousel-control-next:hover { opacity: 0.9; }`)
|
|
2189
2248
|
reg('carousel-indicators', 'components', `.carousel-indicators {
|
|
2249
|
+
--st-carousel-indicator-bg: #fff;
|
|
2190
2250
|
position: absolute; right: 0; bottom: 0; left: 0;
|
|
2191
2251
|
display: flex; justify-content: center;
|
|
2192
2252
|
padding: 0; margin: 0 15%; list-style: none;
|
|
@@ -2194,13 +2254,14 @@ reg('carousel-indicators', 'components', `.carousel-indicators {
|
|
|
2194
2254
|
.carousel-indicators [data-bs-target],
|
|
2195
2255
|
.carousel-indicators button {
|
|
2196
2256
|
width: 30px; height: 3px; margin: 0 3px;
|
|
2197
|
-
background-color:
|
|
2257
|
+
background-color: var(--st-carousel-indicator-bg); border: none; cursor: pointer;
|
|
2198
2258
|
opacity: 0.5; transition: opacity var(--st-duration) var(--st-easing);
|
|
2199
2259
|
}
|
|
2200
2260
|
.carousel-indicators .active { opacity: 1; }`)
|
|
2201
2261
|
reg('carousel-caption', 'components', `.carousel-caption {
|
|
2262
|
+
--st-carousel-caption-color: #fff;
|
|
2202
2263
|
position: absolute; right: 15%; bottom: 1.25rem; left: 15%;
|
|
2203
|
-
padding: 1.25rem; color:
|
|
2264
|
+
padding: 1.25rem; color: var(--st-carousel-caption-color); text-align: center;
|
|
2204
2265
|
}`)
|
|
2205
2266
|
reg('carousel-fade', 'components', `.carousel-fade .carousel-item { opacity: 0; transition: opacity var(--st-duration-slow) var(--st-easing); transform: none; }
|
|
2206
2267
|
.carousel-fade .carousel-item.active { opacity: 1; }`)
|
|
@@ -2297,13 +2358,15 @@ reg('navbar-expand', 'components', `.navbar-expand {
|
|
|
2297
2358
|
.navbar-expand .navbar-collapse { display: flex; flex-basis: auto; }
|
|
2298
2359
|
.navbar-expand .navbar-toggler { display: none; }`)
|
|
2299
2360
|
reg('navbar-dark', 'components', `.navbar-dark {
|
|
2300
|
-
--st-navbar-color:
|
|
2361
|
+
--st-navbar-dark-color: rgba(255,255,255,0.75);
|
|
2362
|
+
--st-navbar-dark-color-hover: rgba(255,255,255,0.9);
|
|
2363
|
+
--st-navbar-dark-toggler-border: rgba(255,255,255,0.1);
|
|
2301
2364
|
background-color: var(--st-dark);
|
|
2302
2365
|
border-color: transparent;
|
|
2303
2366
|
}
|
|
2304
2367
|
.navbar-dark .navbar-brand,
|
|
2305
|
-
.navbar-dark .nav-link { color:
|
|
2306
|
-
.navbar-dark .navbar-toggler { border-color:
|
|
2368
|
+
.navbar-dark .nav-link { color: var(--st-navbar-dark-color-hover); }
|
|
2369
|
+
.navbar-dark .navbar-toggler { border-color: var(--st-navbar-dark-toggler-border); color: var(--st-navbar-dark-color); }`)
|
|
2307
2370
|
reg('navbar-light', 'components', `.navbar-light {
|
|
2308
2371
|
background-color: var(--st-bg);
|
|
2309
2372
|
border-color: var(--st-border);
|
|
@@ -2428,14 +2491,16 @@ reg('tooltip', 'components', `.tooltip {
|
|
|
2428
2491
|
}
|
|
2429
2492
|
.tooltip.show { opacity: 0.9; }`)
|
|
2430
2493
|
reg('tooltip-inner', 'components', `.tooltip-inner {
|
|
2494
|
+
--st-tooltip-color: #fff;
|
|
2495
|
+
--st-tooltip-bg: #000;
|
|
2431
2496
|
max-width: 200px; padding: 0.25rem 0.5rem;
|
|
2432
|
-
color:
|
|
2433
|
-
background-color:
|
|
2497
|
+
color: var(--st-tooltip-color); text-align: center;
|
|
2498
|
+
background-color: var(--st-tooltip-bg); border-radius: var(--st-border-radius);
|
|
2434
2499
|
}`)
|
|
2435
2500
|
reg('bs-tooltip-top', 'components', `.bs-tooltip-top { padding: 4px 0; }
|
|
2436
2501
|
.bs-tooltip-top .tooltip-arrow::before {
|
|
2437
2502
|
top: -1px; border-width: 4px 4px 0;
|
|
2438
|
-
border-top-color: #000;
|
|
2503
|
+
border-top-color: var(--st-tooltip-bg, #000);
|
|
2439
2504
|
}`)
|
|
2440
2505
|
reg('popover', 'components', `.popover {
|
|
2441
2506
|
position: absolute; z-index: var(--st-z-popover, 1070);
|
|
@@ -2654,6 +2719,40 @@ reg('translate-middle-y', 'utilities', `.translate-middle-y { transform: transla
|
|
|
2654
2719
|
// ─── Sizing extras ────────────────────────────────────────────────────
|
|
2655
2720
|
reg('mw-100', 'utilities', `.mw-100 { max-width: 100%; }`)
|
|
2656
2721
|
reg('mh-100', 'utilities', `.mh-100 { max-height: 100%; }`)
|
|
2722
|
+
|
|
2723
|
+
// ─── max-w-* named scale (aligned to Bootstrap container breakpoints) ─
|
|
2724
|
+
// max-w-xs → 320px small phone portrait
|
|
2725
|
+
// max-w-sm → 540px Bootstrap sm container
|
|
2726
|
+
// max-w-md → 720px Bootstrap md container
|
|
2727
|
+
// max-w-lg → 960px Bootstrap lg container
|
|
2728
|
+
// max-w-xl → 1140px Bootstrap xl container
|
|
2729
|
+
// max-w-xxl → 1320px Bootstrap xxl container
|
|
2730
|
+
const MAX_W_SCALE = {
|
|
2731
|
+
'xs': '320px',
|
|
2732
|
+
'sm': '540px',
|
|
2733
|
+
'md': '720px',
|
|
2734
|
+
'lg': '960px',
|
|
2735
|
+
'xl': '1140px',
|
|
2736
|
+
'xxl': '1320px',
|
|
2737
|
+
'full': '100%',
|
|
2738
|
+
'none': 'none',
|
|
2739
|
+
}
|
|
2740
|
+
Object.entries(MAX_W_SCALE).forEach(([k, v]) => {
|
|
2741
|
+
reg(`max-w-${k}`, 'utilities', `.max-w-${k} { max-width: ${v}; }`)
|
|
2742
|
+
})
|
|
2743
|
+
|
|
2744
|
+
// ─── min-w-* ──────────────────────────────────────────────────────────
|
|
2745
|
+
reg('min-w-0', 'utilities', `.min-w-0 { min-width: 0; }`)
|
|
2746
|
+
reg('min-w-full', 'utilities', `.min-w-full { min-width: 100%; }`)
|
|
2747
|
+
reg('min-w-screen', 'utilities', `.min-w-screen { min-width: 100vw; }`)
|
|
2748
|
+
|
|
2749
|
+
// max-h-* and min-h-* common values
|
|
2750
|
+
reg('max-h-full', 'utilities', `.max-h-full { max-height: 100%; }`)
|
|
2751
|
+
reg('max-h-screen', 'utilities', `.max-h-screen { max-height: 100vh; }`)
|
|
2752
|
+
reg('max-h-none', 'utilities', `.max-h-none { max-height: none; }`)
|
|
2753
|
+
reg('min-h-0', 'utilities', `.min-h-0 { min-height: 0; }`)
|
|
2754
|
+
reg('min-h-full', 'utilities', `.min-h-full { min-height: 100%; }`)
|
|
2755
|
+
reg('min-h-screen', 'utilities', `.min-h-screen { min-height: 100vh; }`)
|
|
2657
2756
|
reg('vw-100', 'utilities', `.vw-100 { width: 100vw; }`)
|
|
2658
2757
|
reg('vh-100', 'utilities', `.vh-100 { height: 100vh; }`)
|
|
2659
2758
|
reg('min-vw-100','utilities', `.min-vw-100{ min-width: 100vw; }`)
|
|
@@ -2799,18 +2898,21 @@ reg('tooltip-arrow', 'components', TOOLTIP_ARROW)
|
|
|
2799
2898
|
})
|
|
2800
2899
|
|
|
2801
2900
|
// ─── List utilities ───────────────────────────────────────────────────
|
|
2901
|
+
|
|
2902
|
+
// Remove list styling entirely — resets padding, margin, and list-style
|
|
2802
2903
|
reg('list-unstyled', 'utilities', `.list-unstyled {
|
|
2803
|
-
padding-left:
|
|
2804
|
-
margin-top:
|
|
2904
|
+
padding-left: 0;
|
|
2905
|
+
margin-top: 0;
|
|
2805
2906
|
margin-bottom: 0;
|
|
2806
|
-
list-style:
|
|
2907
|
+
list-style: none;
|
|
2807
2908
|
}`)
|
|
2808
2909
|
|
|
2910
|
+
// Inline list — items sit side by side
|
|
2809
2911
|
reg('list-inline', 'utilities', `.list-inline {
|
|
2810
|
-
padding-left:
|
|
2811
|
-
margin-top:
|
|
2912
|
+
padding-left: 0;
|
|
2913
|
+
margin-top: 0;
|
|
2812
2914
|
margin-bottom: 0;
|
|
2813
|
-
list-style:
|
|
2915
|
+
list-style: none;
|
|
2814
2916
|
}`)
|
|
2815
2917
|
|
|
2816
2918
|
reg('list-inline-item', 'utilities', `.list-inline-item {
|
|
@@ -2821,6 +2923,24 @@ reg('list-inline-item', 'utilities', `.list-inline-item {
|
|
|
2821
2923
|
margin-right: 0.5rem;
|
|
2822
2924
|
}`)
|
|
2823
2925
|
|
|
2926
|
+
// list-style-type variants
|
|
2927
|
+
reg('list-disc', 'utilities', `.list-disc { list-style-type: disc; }`)
|
|
2928
|
+
reg('list-decimal', 'utilities', `.list-decimal { list-style-type: decimal; }`)
|
|
2929
|
+
reg('list-circle', 'utilities', `.list-circle { list-style-type: circle; }`)
|
|
2930
|
+
reg('list-square', 'utilities', `.list-square { list-style-type: square; }`)
|
|
2931
|
+
reg('list-none', 'utilities', `.list-none { list-style-type: none; }`)
|
|
2932
|
+
reg('list-lower-alpha', 'utilities', `.list-lower-alpha { list-style-type: lower-alpha; }`)
|
|
2933
|
+
reg('list-upper-alpha', 'utilities', `.list-upper-alpha { list-style-type: upper-alpha; }`)
|
|
2934
|
+
reg('list-lower-roman', 'utilities', `.list-lower-roman { list-style-type: lower-roman; }`)
|
|
2935
|
+
reg('list-upper-roman', 'utilities', `.list-upper-roman { list-style-type: upper-roman; }`)
|
|
2936
|
+
|
|
2937
|
+
// list-style-position variants
|
|
2938
|
+
reg('list-inside', 'utilities', `.list-inside { list-style-position: inside; }`)
|
|
2939
|
+
reg('list-outside', 'utilities', `.list-outside { list-style-position: outside; }`)
|
|
2940
|
+
|
|
2941
|
+
// Spaced list — adds breathing room between items
|
|
2942
|
+
reg('list-spaced', 'utilities', `.list-spaced > li + li { margin-top: 0.5rem; }`)
|
|
2943
|
+
|
|
2824
2944
|
// ─── Form group ───────────────────────────────────────────────────────
|
|
2825
2945
|
reg('form-group', 'components', `.form-group {
|
|
2826
2946
|
margin-bottom: 1rem;
|
|
@@ -2851,6 +2971,166 @@ Object.entries(OUTLINE_COLOR_MAP).forEach(([k, v]) => {
|
|
|
2851
2971
|
reg(`outline-${n}`, 'utilities', `.outline-${n} { outline-width: ${n}px; }`)
|
|
2852
2972
|
})
|
|
2853
2973
|
|
|
2974
|
+
// ─── Responsive variants ─────────────────────────────────────────────
|
|
2975
|
+
// Breakpoint-prefixed versions of utilities that were previously static.
|
|
2976
|
+
// Pattern: {utility}-{bp}-{value} e.g. flex-md-row, fw-lg-bold, rounded-md-3
|
|
2977
|
+
|
|
2978
|
+
const BP_KEYS = Object.keys(BP_VALUES) // sm md lg xl xxl
|
|
2979
|
+
|
|
2980
|
+
// Flex direction
|
|
2981
|
+
;['row','column','wrap','nowrap','row-reverse','column-reverse'].forEach(v => {
|
|
2982
|
+
const prop = ['wrap','nowrap'].includes(v)
|
|
2983
|
+
? `flex-wrap: ${v === 'nowrap' ? 'nowrap' : 'wrap'};`
|
|
2984
|
+
: `flex-direction: ${v};`
|
|
2985
|
+
BP_KEYS.forEach(bp => {
|
|
2986
|
+
reg(`flex-${bp}-${v}`, 'utilities',
|
|
2987
|
+
mq(bp, `.flex-${bp}-${v} { ${prop} }`))
|
|
2988
|
+
})
|
|
2989
|
+
})
|
|
2990
|
+
|
|
2991
|
+
// Font weight
|
|
2992
|
+
const FW_MAP = {
|
|
2993
|
+
light: '300', lighter: 'lighter', normal: '400',
|
|
2994
|
+
medium: '500', semibold: '600', bold: '700', bolder: 'bolder',
|
|
2995
|
+
}
|
|
2996
|
+
Object.entries(FW_MAP).forEach(([k, v]) => {
|
|
2997
|
+
BP_KEYS.forEach(bp => {
|
|
2998
|
+
reg(`fw-${bp}-${k}`, 'utilities',
|
|
2999
|
+
mq(bp, `.fw-${bp}-${k} { font-weight: ${v}; }`))
|
|
3000
|
+
})
|
|
3001
|
+
})
|
|
3002
|
+
|
|
3003
|
+
// Font style
|
|
3004
|
+
;['italic','normal'].forEach(v => {
|
|
3005
|
+
BP_KEYS.forEach(bp => {
|
|
3006
|
+
reg(`fst-${bp}-${v}`, 'utilities',
|
|
3007
|
+
mq(bp, `.fst-${bp}-${v} { font-style: ${v}; }`))
|
|
3008
|
+
})
|
|
3009
|
+
})
|
|
3010
|
+
|
|
3011
|
+
// Text transform
|
|
3012
|
+
;['uppercase','lowercase','capitalize','none'].forEach(v => {
|
|
3013
|
+
BP_KEYS.forEach(bp => {
|
|
3014
|
+
reg(`text-${bp}-${v}`, 'utilities',
|
|
3015
|
+
mq(bp, `.text-${bp}-${v} { text-transform: ${v}; }`))
|
|
3016
|
+
})
|
|
3017
|
+
})
|
|
3018
|
+
|
|
3019
|
+
// Text decoration
|
|
3020
|
+
;['none','underline','line-through'].forEach(v => {
|
|
3021
|
+
BP_KEYS.forEach(bp => {
|
|
3022
|
+
reg(`text-${bp}-decoration-${v}`, 'utilities',
|
|
3023
|
+
mq(bp, `.text-${bp}-decoration-${v} { text-decoration: ${v}; }`))
|
|
3024
|
+
})
|
|
3025
|
+
})
|
|
3026
|
+
|
|
3027
|
+
// Text wrap
|
|
3028
|
+
;['wrap','nowrap'].forEach(v => {
|
|
3029
|
+
BP_KEYS.forEach(bp => {
|
|
3030
|
+
reg(`text-${bp}-${v}`, 'utilities',
|
|
3031
|
+
mq(bp, `.text-${bp}-${v} { white-space: ${v === 'nowrap' ? 'nowrap' : 'normal'}; }`))
|
|
3032
|
+
})
|
|
3033
|
+
})
|
|
3034
|
+
|
|
3035
|
+
// Border radius
|
|
3036
|
+
const ROUNDED_SCALE = {
|
|
3037
|
+
'0':'0', '1':'0.25rem', '2':'0.375rem', '3':'0.5rem',
|
|
3038
|
+
'4':'0.75rem', '5':'1rem',
|
|
3039
|
+
'pill':'999px', 'circle':'50%',
|
|
3040
|
+
}
|
|
3041
|
+
Object.entries(ROUNDED_SCALE).forEach(([k, v]) => {
|
|
3042
|
+
BP_KEYS.forEach(bp => {
|
|
3043
|
+
reg(`rounded-${bp}-${k}`, 'utilities',
|
|
3044
|
+
mq(bp, `.rounded-${bp}-${k} { border-radius: ${v}; }`))
|
|
3045
|
+
})
|
|
3046
|
+
})
|
|
3047
|
+
BP_KEYS.forEach(bp => {
|
|
3048
|
+
reg(`rounded-${bp}`, 'utilities',
|
|
3049
|
+
mq(bp, `.rounded-${bp} { border-radius: var(--st-border-radius); }`))
|
|
3050
|
+
})
|
|
3051
|
+
|
|
3052
|
+
// Shadow
|
|
3053
|
+
const SHADOW_MAP = {
|
|
3054
|
+
'sm': 'var(--st-shadow-sm)', '': 'var(--st-shadow)',
|
|
3055
|
+
'lg': 'var(--st-shadow-lg)', 'none': 'none',
|
|
3056
|
+
}
|
|
3057
|
+
Object.entries(SHADOW_MAP).forEach(([k, v]) => {
|
|
3058
|
+
const cls = k ? `shadow-${k}` : 'shadow'
|
|
3059
|
+
BP_KEYS.forEach(bp => {
|
|
3060
|
+
reg(`shadow-${bp}${k ? '-' + k : ''}`, 'utilities',
|
|
3061
|
+
mq(bp, `.shadow-${bp}${k ? '-' + k : ''} { box-shadow: ${v}; }`))
|
|
3062
|
+
})
|
|
3063
|
+
})
|
|
3064
|
+
|
|
3065
|
+
// Width
|
|
3066
|
+
Object.entries(SIZE_SCALE).forEach(([k, v]) => {
|
|
3067
|
+
BP_KEYS.forEach(bp => {
|
|
3068
|
+
reg(`w-${bp}-${k}`, 'utilities',
|
|
3069
|
+
mq(bp, `.w-${bp}-${k} { width: ${v}; }`))
|
|
3070
|
+
})
|
|
3071
|
+
})
|
|
3072
|
+
|
|
3073
|
+
// Height
|
|
3074
|
+
Object.entries(SIZE_SCALE).forEach(([k, v]) => {
|
|
3075
|
+
BP_KEYS.forEach(bp => {
|
|
3076
|
+
reg(`h-${bp}-${k}`, 'utilities',
|
|
3077
|
+
mq(bp, `.h-${bp}-${k} { height: ${v}; }`))
|
|
3078
|
+
})
|
|
3079
|
+
})
|
|
3080
|
+
|
|
3081
|
+
// Opacity
|
|
3082
|
+
Object.entries(OPACITY_SCALE).forEach(([k, v]) => {
|
|
3083
|
+
BP_KEYS.forEach(bp => {
|
|
3084
|
+
reg(`opacity-${bp}-${k}`, 'utilities',
|
|
3085
|
+
mq(bp, `.opacity-${bp}-${k} { opacity: ${v}; }`))
|
|
3086
|
+
})
|
|
3087
|
+
})
|
|
3088
|
+
|
|
3089
|
+
// Overflow
|
|
3090
|
+
;['auto','hidden','visible','scroll'].forEach(v => {
|
|
3091
|
+
BP_KEYS.forEach(bp => {
|
|
3092
|
+
reg(`overflow-${bp}-${v}`, 'utilities',
|
|
3093
|
+
mq(bp, `.overflow-${bp}-${v} { overflow: ${v}; }`))
|
|
3094
|
+
reg(`overflow-x-${bp}-${v}`, 'utilities',
|
|
3095
|
+
mq(bp, `.overflow-x-${bp}-${v} { overflow-x: ${v}; }`))
|
|
3096
|
+
reg(`overflow-y-${bp}-${v}`, 'utilities',
|
|
3097
|
+
mq(bp, `.overflow-y-${bp}-${v} { overflow-y: ${v}; }`))
|
|
3098
|
+
})
|
|
3099
|
+
})
|
|
3100
|
+
|
|
3101
|
+
// Position
|
|
3102
|
+
;['static','relative','absolute','fixed','sticky'].forEach(v => {
|
|
3103
|
+
BP_KEYS.forEach(bp => {
|
|
3104
|
+
reg(`position-${bp}-${v}`, 'utilities',
|
|
3105
|
+
mq(bp, `.position-${bp}-${v} { position: ${v}; }`))
|
|
3106
|
+
})
|
|
3107
|
+
})
|
|
3108
|
+
|
|
3109
|
+
// Cursor
|
|
3110
|
+
;['auto','default','pointer','wait','text','move','not-allowed','grab'].forEach(v => {
|
|
3111
|
+
BP_KEYS.forEach(bp => {
|
|
3112
|
+
reg(`cursor-${bp}-${v}`, 'utilities',
|
|
3113
|
+
mq(bp, `.cursor-${bp}-${v} { cursor: ${v}; }`))
|
|
3114
|
+
})
|
|
3115
|
+
})
|
|
3116
|
+
|
|
3117
|
+
// Line height
|
|
3118
|
+
const LH_MAP = { '1':'1', 'sm':'1.25', 'base':'1.5', 'lg':'2' }
|
|
3119
|
+
Object.entries(LH_MAP).forEach(([k, v]) => {
|
|
3120
|
+
BP_KEYS.forEach(bp => {
|
|
3121
|
+
reg(`lh-${bp}-${k}`, 'utilities',
|
|
3122
|
+
mq(bp, `.lh-${bp}-${k} { line-height: ${v}; }`))
|
|
3123
|
+
})
|
|
3124
|
+
})
|
|
3125
|
+
|
|
3126
|
+
// Visibility
|
|
3127
|
+
BP_KEYS.forEach(bp => {
|
|
3128
|
+
reg(`visible-${bp}`, 'utilities',
|
|
3129
|
+
mq(bp, `.visible-${bp} { visibility: visible; }`))
|
|
3130
|
+
reg(`invisible-${bp}`, 'utilities',
|
|
3131
|
+
mq(bp, `.invisible-${bp} { visibility: hidden; }`))
|
|
3132
|
+
})
|
|
3133
|
+
|
|
2854
3134
|
// ─── Arbitrary value patterns — regex fallback ────────────────────────
|
|
2855
3135
|
// Only used when no exact match found in EXACT_MAP
|
|
2856
3136
|
|
|
@@ -2864,10 +3144,15 @@ const ARBITRARY_PATTERNS = [
|
|
|
2864
3144
|
const decl = props.map(p => ` ${p}: ${val}${i};`).join('\n')
|
|
2865
3145
|
return { layer: 'utilities', css: `.${escapeClass(m[0])} {\n${decl}\n}` }
|
|
2866
3146
|
}},
|
|
2867
|
-
// Text
|
|
3147
|
+
// Text arbitrary: text-[#ff0000] → color, text-[15px] → font-size
|
|
3148
|
+
// Values ending in a CSS length unit are font-size; everything else is color.
|
|
2868
3149
|
{ re: /^(!?)text-\[(.+)\]$/, fn: (m) => {
|
|
2869
|
-
const i
|
|
2870
|
-
|
|
3150
|
+
const i = m[1] ? ' !important' : ''
|
|
3151
|
+
const val = m[2]
|
|
3152
|
+
const prop = /^[\d.]+(px|rem|em|%|vw|vh|ch|ex|pt|cm|mm)$/.test(val)
|
|
3153
|
+
? 'font-size'
|
|
3154
|
+
: 'color'
|
|
3155
|
+
return { layer: 'utilities', css: `.${escapeClass(m[0])} { ${prop}: ${val}${i}; }` }
|
|
2871
3156
|
}},
|
|
2872
3157
|
// BG arbitrary: bg-[#ff0000]
|
|
2873
3158
|
{ re: /^(!?)bg-\[(.+)\]$/, fn: (m) => {
|
|
@@ -2889,6 +3174,26 @@ const ARBITRARY_PATTERNS = [
|
|
|
2889
3174
|
const i = m[1] ? ' !important' : ''
|
|
2890
3175
|
return { layer: 'utilities', css: `.${escapeClass(m[0])} { height: ${m[2]}${i}; }` }
|
|
2891
3176
|
}},
|
|
3177
|
+
// Max-width arbitrary: max-w-[440px]
|
|
3178
|
+
{ re: /^(!?)max-w-\[(.+)\]$/, fn: (m) => {
|
|
3179
|
+
const i = m[1] ? ' !important' : ''
|
|
3180
|
+
return { layer: 'utilities', css: `.${escapeClass(m[0])} { max-width: ${m[2]}${i}; }` }
|
|
3181
|
+
}},
|
|
3182
|
+
// Min-width arbitrary: min-w-[200px]
|
|
3183
|
+
{ re: /^(!?)min-w-\[(.+)\]$/, fn: (m) => {
|
|
3184
|
+
const i = m[1] ? ' !important' : ''
|
|
3185
|
+
return { layer: 'utilities', css: `.${escapeClass(m[0])} { min-width: ${m[2]}${i}; }` }
|
|
3186
|
+
}},
|
|
3187
|
+
// Max-height arbitrary: max-h-[500px]
|
|
3188
|
+
{ re: /^(!?)max-h-\[(.+)\]$/, fn: (m) => {
|
|
3189
|
+
const i = m[1] ? ' !important' : ''
|
|
3190
|
+
return { layer: 'utilities', css: `.${escapeClass(m[0])} { max-height: ${m[2]}${i}; }` }
|
|
3191
|
+
}},
|
|
3192
|
+
// Min-height arbitrary: min-h-[300px]
|
|
3193
|
+
{ re: /^(!?)min-h-\[(.+)\]$/, fn: (m) => {
|
|
3194
|
+
const i = m[1] ? ' !important' : ''
|
|
3195
|
+
return { layer: 'utilities', css: `.${escapeClass(m[0])} { min-height: ${m[2]}${i}; }` }
|
|
3196
|
+
}},
|
|
2892
3197
|
// Opacity arbitrary: opacity-[0.3]
|
|
2893
3198
|
{ re: /^(!?)opacity-\[(.+)\]$/, fn: (m) => {
|
|
2894
3199
|
const i = m[1] ? ' !important' : ''
|