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/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))