strata-css 1.0.0
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 +62 -0
- package/LICENSE +21 -0
- package/README.md +730 -0
- package/bin/strata.js +139 -0
- package/index.d.ts +70 -0
- package/package.json +68 -0
- package/src/components/modules/modal.js +123 -0
- package/src/components/modules/skeleton.js +334 -0
- package/src/components/strata.manifest.js +15 -0
- package/src/generator/generator.js +113 -0
- package/src/index.js +172 -0
- package/src/layers/base.js +571 -0
- package/src/registry/breakpoints.js +54 -0
- package/src/registry/registry.js +2898 -0
- package/src/scanner/scanner.js +106 -0
- package/strata.css +3 -0
package/bin/strata.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
'use strict'
|
|
4
|
+
|
|
5
|
+
const fs = require('fs')
|
|
6
|
+
const path = require('path')
|
|
7
|
+
const chokidar = require('chokidar')
|
|
8
|
+
const strata = require('../src/index')
|
|
9
|
+
const { getWatchFiles } = require('../src/scanner/scanner')
|
|
10
|
+
|
|
11
|
+
const args = process.argv.slice(2)
|
|
12
|
+
const cwd = process.cwd()
|
|
13
|
+
|
|
14
|
+
function loadConfig() {
|
|
15
|
+
const configPath = path.resolve(cwd, 'strata.config.js')
|
|
16
|
+
try { return require(configPath) } catch { return {} }
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// ─── JS minifier ──────────────────────────────────────────────────────
|
|
20
|
+
// Strips block comments (preserving /*! banners), line comments,
|
|
21
|
+
// and collapses unnecessary whitespace — no external dependency needed.
|
|
22
|
+
|
|
23
|
+
function minifyJS(src) {
|
|
24
|
+
return src
|
|
25
|
+
// preserve /*! banner */ comments, strip all other /* ... */ blocks
|
|
26
|
+
.replace(/\/\*(?!!)([\s\S]*?)\*\//g, '')
|
|
27
|
+
// strip // line comments (not inside strings — conservative: only at line start or after whitespace)
|
|
28
|
+
.replace(/(?:^|\s)\/\/[^\n]*/gm, '')
|
|
29
|
+
// collapse runs of whitespace/newlines to a single space
|
|
30
|
+
.replace(/[ \t]+/g, ' ')
|
|
31
|
+
.replace(/\n\s*\n+/g, '\n')
|
|
32
|
+
.trim()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ─── Build ────────────────────────────────────────────────────────────
|
|
36
|
+
// --watch → unminified CSS, unminified JS (fast rebuilds for dev)
|
|
37
|
+
// --build → unminified CSS, minified JS (production-ready JS)
|
|
38
|
+
// --minify → minified CSS + minified JS (smallest possible output)
|
|
39
|
+
|
|
40
|
+
async function build(cssMinify = false, jsMinify = true) {
|
|
41
|
+
const config = loadConfig()
|
|
42
|
+
const inputFile = config.input || path.join(cwd, 'strata.css')
|
|
43
|
+
const outputFile = config.output || path.join(cwd, 'dist', 'strata.output.css')
|
|
44
|
+
|
|
45
|
+
const start = process.hrtime.bigint()
|
|
46
|
+
const css = await strata.build(inputFile, outputFile, { cwd, sourceMap: !cssMinify })
|
|
47
|
+
|
|
48
|
+
// CSS minification
|
|
49
|
+
if (cssMinify) {
|
|
50
|
+
const cssnano = require('cssnano')
|
|
51
|
+
const postcss = require('postcss')
|
|
52
|
+
const result = await postcss([cssnano({ preset: 'default' })]).process(css, { from: outputFile })
|
|
53
|
+
fs.writeFileSync(outputFile, result.css)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Bundle + optionally minify JS components
|
|
57
|
+
const componentsDir = path.join(__dirname, '..', 'src', 'components', 'modules')
|
|
58
|
+
const jsDest = path.join(path.dirname(outputFile), 'strata.components.js')
|
|
59
|
+
if (fs.existsSync(componentsDir)) {
|
|
60
|
+
const files = fs.readdirSync(componentsDir).filter(f => f.endsWith('.js')).sort()
|
|
61
|
+
const banner = `/*! Strata Components — built ${new Date().toISOString().slice(0,10)} */\n`
|
|
62
|
+
const raw = files.map(f => fs.readFileSync(path.join(componentsDir, f), 'utf8')).join('\n')
|
|
63
|
+
const output = jsMinify ? banner + minifyJS(raw) : banner + raw
|
|
64
|
+
fs.mkdirSync(path.dirname(jsDest), { recursive: true })
|
|
65
|
+
fs.writeFileSync(jsDest, output)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const ms = (Number(process.hrtime.bigint() - start) / 1_000_000).toFixed(2)
|
|
69
|
+
const cssSize = (Buffer.byteLength(fs.readFileSync(outputFile)) / 1024).toFixed(2)
|
|
70
|
+
const jsSize = fs.existsSync(jsDest)
|
|
71
|
+
? (Buffer.byteLength(fs.readFileSync(jsDest)) / 1024).toFixed(2)
|
|
72
|
+
: '0'
|
|
73
|
+
console.log(`[Strata] ✓ Built → ${outputFile} (CSS ${cssSize} KB, JS ${jsSize} KB) in ${ms}ms`)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ─── Watch ────────────────────────────────────────────────────────────
|
|
77
|
+
async function watch() {
|
|
78
|
+
console.log('[Strata] Starting in watch mode...')
|
|
79
|
+
await build(false, false) // unminified for dev
|
|
80
|
+
|
|
81
|
+
const config = loadConfig()
|
|
82
|
+
const contentGlobs = config.content || ['./src/**/*.{html,jsx,tsx,vue,astro,svelte,js,ts}']
|
|
83
|
+
const inputFile = config.input || path.join(cwd, 'strata.css')
|
|
84
|
+
const watchFiles = [inputFile, ...getWatchFiles(contentGlobs)]
|
|
85
|
+
|
|
86
|
+
const watcher = chokidar.watch(watchFiles, { ignoreInitial: true, persistent: true })
|
|
87
|
+
|
|
88
|
+
watcher.on('change', async (filePath) => {
|
|
89
|
+
console.log(`[Strata] Changed: ${path.relative(cwd, filePath)}`)
|
|
90
|
+
strata.invalidate(filePath)
|
|
91
|
+
await build(false, false)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
watcher.on('add', async (filePath) => {
|
|
95
|
+
console.log(`[Strata] Added: ${path.relative(cwd, filePath)}`)
|
|
96
|
+
strata.invalidate(filePath)
|
|
97
|
+
await build(false, false)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
console.log('[Strata] Watching for changes...')
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ─── Init ─────────────────────────────────────────────────────────────
|
|
104
|
+
function init() {
|
|
105
|
+
console.log('[Strata] Initializing project...')
|
|
106
|
+
|
|
107
|
+
const files = {
|
|
108
|
+
'strata.config.js': `module.exports = {\n content: ["./src/**/*.{html,jsx,tsx,vue,astro,svelte,js,ts}"],\n input: "./strata.css",\n output: "./dist/strata.output.css"\n}\n`,
|
|
109
|
+
'strata.css': `@strata base;\n@strata components;\n@strata utilities;\n`,
|
|
110
|
+
'postcss.config.js':`module.exports = { plugins: [require('strata-css'), require('autoprefixer')] }\n`,
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
for (const [filename, content] of Object.entries(files)) {
|
|
114
|
+
const filePath = path.join(cwd, filename)
|
|
115
|
+
if (fs.existsSync(filePath)) {
|
|
116
|
+
console.log(`[Strata] Skipped (exists): ${filename}`)
|
|
117
|
+
} else {
|
|
118
|
+
fs.writeFileSync(filePath, content)
|
|
119
|
+
console.log(`[Strata] Created: ${filename}`)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
fs.mkdirSync(path.join(cwd, 'dist'), { recursive: true })
|
|
124
|
+
console.log('\n[Strata] Done! Run: npm run dev')
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ─── Run ──────────────────────────────────────────────────────────────
|
|
128
|
+
if (args[0] === 'init') init()
|
|
129
|
+
else if (args.includes('--watch')) watch()
|
|
130
|
+
else if (args.includes('--minify')) build(true, true)
|
|
131
|
+
else if (args.includes('--build')) build(false, true)
|
|
132
|
+
else console.log(`
|
|
133
|
+
Strata CSS
|
|
134
|
+
|
|
135
|
+
strata init scaffold a new project
|
|
136
|
+
strata --watch development mode (unminified, fast rebuild)
|
|
137
|
+
strata --build production build (minified JS, readable CSS)
|
|
138
|
+
strata --minify production build (minified CSS + JS, smallest output)
|
|
139
|
+
`)
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strata CSS — TypeScript Declarations
|
|
3
|
+
* https://github.com/AftabIbrahimKazi/strata
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface StrataThemeConfig {
|
|
7
|
+
/** Add or override breakpoints. Key is the breakpoint name, value is the min-width (e.g. "1600px"). */
|
|
8
|
+
breakpoints?: Record<string, string>
|
|
9
|
+
/** Override semantic color values. Key is the color name (e.g. "primary"), value is a CSS color string. */
|
|
10
|
+
colors?: Record<string, string>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface StrataComponentsConfig {
|
|
14
|
+
/** Component names to exclude from the generated CSS. */
|
|
15
|
+
exclude?: string[]
|
|
16
|
+
/** Component names to explicitly include (when using an allowlist approach). */
|
|
17
|
+
include?: string[]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Configuration object for strata.config.js */
|
|
21
|
+
export interface StrataConfig {
|
|
22
|
+
/**
|
|
23
|
+
* Glob patterns pointing to files Strata should scan for class names.
|
|
24
|
+
* @example ["./src/**\/*.{html,jsx,tsx,vue,astro,svelte}"]
|
|
25
|
+
*/
|
|
26
|
+
content: string[]
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Path to the entry CSS file containing @strata directives.
|
|
30
|
+
* @default "./strata.css"
|
|
31
|
+
*/
|
|
32
|
+
input?: string
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Path where Strata writes the generated CSS output.
|
|
36
|
+
* @default "./dist/strata.output.css"
|
|
37
|
+
*/
|
|
38
|
+
output?: string
|
|
39
|
+
|
|
40
|
+
/** Theme overrides — extend breakpoints or override color values. */
|
|
41
|
+
theme?: StrataThemeConfig
|
|
42
|
+
|
|
43
|
+
/** Include or exclude specific components from the generated output. */
|
|
44
|
+
components?: StrataComponentsConfig
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Options passed to the build function. */
|
|
48
|
+
export interface StrataBuildOptions {
|
|
49
|
+
/** Working directory used to resolve config and content paths. Defaults to process.cwd(). */
|
|
50
|
+
cwd?: string
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Run a full Strata build — scans content files, generates CSS, writes to output path.
|
|
55
|
+
* Equivalent to `strata --build` from the CLI.
|
|
56
|
+
*/
|
|
57
|
+
export function build(
|
|
58
|
+
input: string,
|
|
59
|
+
output: string,
|
|
60
|
+
options?: StrataBuildOptions
|
|
61
|
+
): Promise<void>
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Invalidate the internal build cache, forcing a full cold rebuild on the next build() call.
|
|
65
|
+
* Call this when content files change programmatically.
|
|
66
|
+
*/
|
|
67
|
+
export function invalidate(): void
|
|
68
|
+
|
|
69
|
+
/** Marks this module as a PostCSS plugin (required by PostCSS plugin discovery). */
|
|
70
|
+
export const postcss: true
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "strata-css",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"_versioningNote": "Stable: 1.0.0 / 1.1.0 / 2.0.0 | Beta: 1.1.0-beta.1 / 1.1.0-beta.2",
|
|
5
|
+
"description": "A modern CSS framework combining Bootstrap components with Tailwind JIT processing",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"types": "index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./src/index.js",
|
|
10
|
+
"./package.json": "./package.json"
|
|
11
|
+
},
|
|
12
|
+
"bin": {
|
|
13
|
+
"strata": "bin/strata.js"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"src/",
|
|
17
|
+
"bin/",
|
|
18
|
+
"strata.css",
|
|
19
|
+
"index.d.ts",
|
|
20
|
+
"README.md",
|
|
21
|
+
"LICENSE",
|
|
22
|
+
"CHANGELOG.md"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"dev": "node bin/strata.js --watch",
|
|
26
|
+
"build": "node bin/strata.js --build",
|
|
27
|
+
"minify": "node bin/strata.js --minify",
|
|
28
|
+
"benchmark": "node --expose-gc benchmark/run.js",
|
|
29
|
+
"publish:stable": "npm publish --tag latest",
|
|
30
|
+
"publish:beta": "npm publish --tag beta"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"css",
|
|
34
|
+
"framework",
|
|
35
|
+
"postcss",
|
|
36
|
+
"postcss-plugin",
|
|
37
|
+
"jit",
|
|
38
|
+
"tailwind",
|
|
39
|
+
"tailwindcss",
|
|
40
|
+
"bootstrap",
|
|
41
|
+
"component-library",
|
|
42
|
+
"utility-first",
|
|
43
|
+
"theming",
|
|
44
|
+
"dark-mode",
|
|
45
|
+
"css-framework"
|
|
46
|
+
],
|
|
47
|
+
"author": "Aftab Ibrahim Kazi",
|
|
48
|
+
"license": "MIT",
|
|
49
|
+
"homepage": "https://aftabibrahimkazi.github.io/strata",
|
|
50
|
+
"repository": {
|
|
51
|
+
"type": "git",
|
|
52
|
+
"url": "git+https://github.com/AftabIbrahimKazi/strata.git"
|
|
53
|
+
},
|
|
54
|
+
"bugs": {
|
|
55
|
+
"url": "https://github.com/AftabIbrahimKazi/strata/issues"
|
|
56
|
+
},
|
|
57
|
+
"type": "commonjs",
|
|
58
|
+
"engines": {
|
|
59
|
+
"node": ">=18.0.0"
|
|
60
|
+
},
|
|
61
|
+
"dependencies": {
|
|
62
|
+
"autoprefixer": "^10.5.0",
|
|
63
|
+
"chokidar": "^5.0.0",
|
|
64
|
+
"cssnano": "^8.0.1",
|
|
65
|
+
"glob": "^13.0.6",
|
|
66
|
+
"postcss": "^8.5.14"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Strata Modal Component
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* Trigger: <button data-st-toggle="modal" data-st-target="#myModal">Open</button>
|
|
6
|
+
* Dismiss: <button data-st-dismiss="modal">Close</button>
|
|
7
|
+
* Static: <div class="modal" data-st-backdrop="static" ...>
|
|
8
|
+
* API: Strata.Modal.open('#myModal') / Strata.Modal.close()
|
|
9
|
+
*
|
|
10
|
+
* Events fired on document:
|
|
11
|
+
* st:modal:open — detail: { modal }
|
|
12
|
+
* st:modal:close — detail: { modal }
|
|
13
|
+
*/
|
|
14
|
+
;(function (win, doc) {
|
|
15
|
+
'use strict'
|
|
16
|
+
|
|
17
|
+
var currentModal = null
|
|
18
|
+
var backdrop = null
|
|
19
|
+
|
|
20
|
+
function ensureBackdrop() {
|
|
21
|
+
if (!backdrop) {
|
|
22
|
+
backdrop = doc.createElement('div')
|
|
23
|
+
backdrop.className = 'modal-backdrop'
|
|
24
|
+
doc.body.appendChild(backdrop)
|
|
25
|
+
}
|
|
26
|
+
return backdrop
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function lockScroll() {
|
|
30
|
+
var sbw = win.innerWidth - doc.documentElement.clientWidth
|
|
31
|
+
doc.body.style.setProperty('--st-scrollbar-width', sbw + 'px')
|
|
32
|
+
doc.body.classList.add('modal-open')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function unlockScroll() {
|
|
36
|
+
doc.body.classList.remove('modal-open')
|
|
37
|
+
doc.body.style.removeProperty('--st-scrollbar-width')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function openModal(modal) {
|
|
41
|
+
if (currentModal === modal) return
|
|
42
|
+
if (currentModal) closeModal()
|
|
43
|
+
|
|
44
|
+
currentModal = modal
|
|
45
|
+
modal.setAttribute('data-st-visible', 'true')
|
|
46
|
+
modal.removeAttribute('aria-hidden')
|
|
47
|
+
modal.setAttribute('aria-modal', 'true')
|
|
48
|
+
|
|
49
|
+
var bd = ensureBackdrop()
|
|
50
|
+
void bd.offsetHeight // force reflow so transition plays
|
|
51
|
+
bd.setAttribute('data-st-visible', 'true')
|
|
52
|
+
|
|
53
|
+
lockScroll()
|
|
54
|
+
|
|
55
|
+
var focusTarget = modal.querySelector('[autofocus]') ||
|
|
56
|
+
modal.querySelector('.modal-content')
|
|
57
|
+
if (focusTarget) setTimeout(function() { focusTarget.focus() }, 50)
|
|
58
|
+
|
|
59
|
+
doc.dispatchEvent(new CustomEvent('st:modal:open', { detail: { modal: modal } }))
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function closeModal() {
|
|
63
|
+
if (!currentModal) return
|
|
64
|
+
|
|
65
|
+
var modal = currentModal
|
|
66
|
+
currentModal = null
|
|
67
|
+
|
|
68
|
+
modal.setAttribute('data-st-visible', 'false')
|
|
69
|
+
modal.setAttribute('aria-hidden', 'true')
|
|
70
|
+
modal.removeAttribute('aria-modal')
|
|
71
|
+
|
|
72
|
+
if (backdrop) backdrop.setAttribute('data-st-visible', 'false')
|
|
73
|
+
|
|
74
|
+
unlockScroll()
|
|
75
|
+
|
|
76
|
+
doc.dispatchEvent(new CustomEvent('st:modal:close', { detail: { modal: modal } }))
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
doc.addEventListener('click', function(e) {
|
|
80
|
+
var trigger = e.target.closest('[data-st-toggle="modal"]')
|
|
81
|
+
if (trigger) {
|
|
82
|
+
var sel = trigger.getAttribute('data-st-target') || trigger.getAttribute('href')
|
|
83
|
+
if (sel) {
|
|
84
|
+
var target = doc.querySelector(sel)
|
|
85
|
+
if (target) openModal(target)
|
|
86
|
+
}
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (e.target.closest('[data-st-dismiss="modal"]')) {
|
|
91
|
+
closeModal()
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (currentModal && e.target === currentModal) {
|
|
96
|
+
var isStatic = currentModal.getAttribute('data-st-backdrop') === 'static'
|
|
97
|
+
if (isStatic) {
|
|
98
|
+
currentModal.classList.add('modal-static')
|
|
99
|
+
var m = currentModal
|
|
100
|
+
setTimeout(function() { m.classList.remove('modal-static') }, 300)
|
|
101
|
+
} else {
|
|
102
|
+
closeModal()
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
doc.addEventListener('keydown', function(e) {
|
|
108
|
+
if (e.key === 'Escape' && currentModal) {
|
|
109
|
+
var isStatic = currentModal.getAttribute('data-st-backdrop') === 'static'
|
|
110
|
+
if (!isStatic) closeModal()
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
win.Strata = win.Strata || {}
|
|
115
|
+
win.Strata.Modal = {
|
|
116
|
+
open: function(selector) {
|
|
117
|
+
var el = typeof selector === 'string' ? doc.querySelector(selector) : selector
|
|
118
|
+
if (el) openModal(el)
|
|
119
|
+
},
|
|
120
|
+
close: closeModal
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
}(window, document))
|