You can now use any of the Basecoat classes in your project (e.g. btn, card, input, etc). Read the documentation about each component to get started (e.g. button , card , input , etc).
Some components (e.g. Select ) require JavaScript code to work .
@@ -75,26 +122,21 @@ toc:
-
Some components require JavaScript code to work.
-
The documentation provides Alpine.js code snippets to cover that. Make sure to install Alpine.js if you decide to use them.
-
For simple components like Accordion or Slider , you will notice some inline Alpine.js code that you can just copy along with the rest of the HTML.
-
For more complex components (Dialog , Dropdown Menu , Popover , Select , Tabs and Toast ), you have one of 2 options:
+
Some components may require JavaScript code to work.
+
+
If a component requires JavaScript, the documentation page will provide instructions. There are 2 options to add the JavaScript code to your project:
+
+
+
A few things to keep in mind:
+
+ Most components (e.g. Button , Card , Input , etc) do not require any JavaScript at all to work.
+ Some of the JavaScript is optional. For example the Dropdown Menu component suggest to add the CSS anchor positioning polyfill to support older browser, as well as a dropdown-menu.js to improve accessiblity (WAI-ARIA) and usability (keyboard navigation).
+ Only a handful of components require JavaScript to work (e.g. Sidebar , Toast , ...).
-
You are also welcome to write your own JavaScript.
-
All of the components try and implement WAI-ARIA standards to make them accessible.
@@ -175,14 +217,25 @@ toc:
-
You can override default styles using Tailwind:
+
+ You can override default styles using Tailwind:
+
+
{% set code %}
Click me {% endset %}
{{ code_block(code, "html") }}
-
You can also obviously extend and/or override the existing styles with your own Tailwind files:
+
+
+ You can also extend or override the existing styles in your own Tailwind files:
+
+
{% set code %}@import "tailwindcss";
@import "basecoat-css";
@import "theme.css";{% endset %}
{{ code_block(code, "css") }}
-
You can also use use the theme variables to customize many aspects of the components (colors, fonts, sizes, etc).
-
If you want to make more drastic changes , I recommend copying the basecoat.css file into your project and making your changes there.
+
+
+ More importantly, you can use the theme variables to customize many aspects of the components (colors, fonts, sizes, etc).
+
+ If you want to make more drastic changes , you can copy the basecoat.css file into your project and make your changes there.
+
diff --git a/package-lock.json b/package-lock.json
index 3c55bc4..349ef6b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "basecoat",
- "version": "0.0.1",
+ "version": "0.2.0-beta.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "basecoat",
- "version": "0.0.1",
+ "version": "0.2.0-beta.1",
"license": "MIT",
"workspaces": [
"packages/*"
@@ -14,10 +14,12 @@
"devDependencies": {
"@11ty/eleventy": "^3.0.0",
"@grimlink/eleventy-plugin-lucide-icons": "^2.1.4",
+ "@oddbird/css-anchor-positioning": "^0.6.0",
"@tailwindcss/cli": "^4.1.3",
"concurrently": "^9.1.2",
"prettier": "^3.5.3",
- "tailwindcss": "^4.1.3"
+ "tailwindcss": "^4.1.3",
+ "terser": "^5.42.0"
}
},
"node_modules/@11ty/dependency-tree": {
@@ -243,6 +245,34 @@
"slash": "^1.0.0"
}
},
+ "node_modules/@floating-ui/core": {
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.1.tgz",
+ "integrity": "sha512-azI0DrjMMfIug/ExbBaeDVJXcY0a7EPvPjb2xAJPa4HeimBX+Z18HK8QQR3jb6356SnDDdxx+hinMLcJEDdOjw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/utils": "^0.2.9"
+ }
+ },
+ "node_modules/@floating-ui/dom": {
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.1.tgz",
+ "integrity": "sha512-cwsmW/zyw5ltYTUeeYJ60CnQuPqmGwuGVhG9w0PRaRKkAyi38BT5CKrpIbb+jtahSwUl04cWzSx9ZOIxeS6RsQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/core": "^1.7.1",
+ "@floating-ui/utils": "^0.2.9"
+ }
+ },
+ "node_modules/@floating-ui/utils": {
+ "version": "0.2.9",
+ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz",
+ "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@grimlink/eleventy-plugin-lucide-icons": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@grimlink/eleventy-plugin-lucide-icons/-/eleventy-plugin-lucide-icons-2.1.4.tgz",
@@ -630,6 +660,70 @@
"node": ">=12"
}
},
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.8",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
+ "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/set-array": "^1.2.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/set-array": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+ "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/source-map": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
+ "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.25"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+ "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.25",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+ "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -668,6 +762,19 @@
"node": ">= 8"
}
},
+ "node_modules/@oddbird/css-anchor-positioning": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/@oddbird/css-anchor-positioning/-/css-anchor-positioning-0.6.0.tgz",
+ "integrity": "sha512-5r6AhCHSW/bU45gCwOdaei04WPn2E7oeztC8o0z6euyAPBbAeiYQda5ByIiMRfOf13sa8rVqiCsKpwx/QNyHbw==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@floating-ui/dom": "^1.6.13",
+ "@types/css-tree": "^2.3.10",
+ "css-tree": "^3.1.0",
+ "nanoid": "^5.1.5"
+ }
+ },
"node_modules/@parcel/watcher": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz",
@@ -1294,6 +1401,13 @@
"node": ">= 10"
}
},
+ "node_modules/@types/css-tree": {
+ "version": "2.3.10",
+ "resolved": "https://registry.npmjs.org/@types/css-tree/-/css-tree-2.3.10.tgz",
+ "integrity": "sha512-WcaBazJ84RxABvRttQjjFWgTcHvZR9jGr0Y3hccPkHjFyk/a3N8EuxjKr+QfrwjoM5b1yI1Uj1i7EzOAAwBwag==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/a-sync-waterfall": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz",
@@ -1535,6 +1649,13 @@
"node": ">=8"
}
},
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -1759,6 +1880,20 @@
"node": ">= 8"
}
},
+ "node_modules/css-tree": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz",
+ "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mdn-data": "2.12.2",
+ "source-map-js": "^1.0.1"
+ },
+ "engines": {
+ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
+ }
+ },
"node_modules/debug": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
@@ -3012,6 +3147,13 @@
"node": ">=0.10.0"
}
},
+ "node_modules/mdn-data": {
+ "version": "2.12.2",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz",
+ "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==",
+ "dev": true,
+ "license": "CC0-1.0"
+ },
"node_modules/mdurl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
@@ -3168,6 +3310,25 @@
"node": "^18.17.0 || >=20.5.0"
}
},
+ "node_modules/nanoid": {
+ "version": "5.1.5",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.5.tgz",
+ "integrity": "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.js"
+ },
+ "engines": {
+ "node": "^18 || >=20"
+ }
+ },
"node_modules/node-addon-api": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
@@ -3703,6 +3864,37 @@
"node": ">=8.0.0"
}
},
+ "node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
"node_modules/sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
@@ -3880,6 +4072,32 @@
"node": ">=6"
}
},
+ "node_modules/terser": {
+ "version": "5.42.0",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.42.0.tgz",
+ "integrity": "sha512-UYCvU9YQW2f/Vwl+P0GfhxJxbUGLwd+5QrrGgLajzWAtC/23AX0vcise32kkP7Eu0Wu9VlzzHAXkLObgjQfFlQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@jridgewell/source-map": "^0.3.3",
+ "acorn": "^8.14.0",
+ "commander": "^2.20.0",
+ "source-map-support": "~0.5.20"
+ },
+ "bin": {
+ "terser": "bin/terser"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/terser/node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
@@ -4207,7 +4425,7 @@
},
"packages/cli": {
"name": "basecoat-cli",
- "version": "0.1.0",
+ "version": "0.1.1",
"license": "MIT",
"dependencies": {
"commander": "^13.1.0",
@@ -4220,7 +4438,7 @@
},
"packages/css": {
"name": "basecoat-css",
- "version": "0.1.0",
+ "version": "0.1.2",
"license": "MIT"
}
}
diff --git a/package.json b/package.json
index 8b88dd9..9768e80 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "basecoat",
- "version": "0.1.0",
+ "version": "0.2.0-beta.1",
"description": "shadcn/ui for the rest of us",
"author": "hunvreus",
"type": "module",
@@ -52,9 +52,11 @@
"devDependencies": {
"@11ty/eleventy": "^3.0.0",
"@grimlink/eleventy-plugin-lucide-icons": "^2.1.4",
+ "@oddbird/css-anchor-positioning": "^0.6.0",
"@tailwindcss/cli": "^4.1.3",
"concurrently": "^9.1.2",
"prettier": "^3.5.3",
- "tailwindcss": "^4.1.3"
+ "tailwindcss": "^4.1.3",
+ "terser": "^5.42.0"
}
}
diff --git a/scripts/build.js b/scripts/build.js
index 97c6fa5..01899e9 100644
--- a/scripts/build.js
+++ b/scripts/build.js
@@ -1,6 +1,10 @@
import fs from 'fs/promises';
import path from 'path';
import { fileURLToPath } from 'url';
+import { exec } from 'child_process';
+import { promisify } from 'util';
+
+const execPromise = promisify(exec);
// Resolve project root, assuming this script is in
/scripts/
const __filename = fileURLToPath(import.meta.url);
@@ -90,8 +94,66 @@ async function build() {
await fs.copyFile(path.join(cliPackageDir, 'index.js'), path.join(cliDistDir, 'index.js'));
console.log(`Copied CLI index.js to ${cliDistDir}`);
- await copyDirRecursive(srcJsDir, path.join(cliDistAssetsDir, 'js'));
- console.log(`Copied JS assets to ${path.join(cliDistAssetsDir, 'js')}`);
+ // JS files minification and copy
+ const jsFiles = await fs.readdir(srcJsDir);
+ const cliJsDistDir = path.join(cliDistAssetsDir, 'js');
+ const cssJsDistDir = path.join(cssDistDir, 'js');
+ await ensureDir(cliJsDistDir);
+ await ensureDir(cssJsDistDir);
+ console.log('Copying and minifying JS files...');
+
+ for (const jsFile of jsFiles) {
+ if (path.extname(jsFile) === '.js') {
+ const srcFile = path.join(srcJsDir, jsFile);
+ const baseName = path.basename(jsFile, '.js');
+ const minifiedFileName = `${baseName}.min.js`;
+
+ // Copy original file to both destinations
+ await fs.copyFile(srcFile, path.join(cliJsDistDir, jsFile));
+ await fs.copyFile(srcFile, path.join(cssJsDistDir, jsFile));
+
+ // Create and copy minified file to both destinations
+ const cliDestMinFile = path.join(cliJsDistDir, minifiedFileName);
+ await execPromise(`npx terser ${srcFile} -o ${cliDestMinFile} --compress --mangle`);
+ await fs.copyFile(cliDestMinFile, path.join(cssJsDistDir, minifiedFileName));
+ }
+ }
+
+ // Handle anchor positioning polyfill
+ const polyfillSrc = path.join(projectRoot, 'node_modules', '@oddbird', 'css-anchor-positioning', 'dist', 'css-anchor-positioning.js');
+ const polyfillDest = 'css-anchor-positioning.js';
+ const polyfillMinDest = 'css-anchor-positioning.min.js';
+
+ // Copy original polyfill
+ await fs.copyFile(polyfillSrc, path.join(cliJsDistDir, polyfillDest));
+ await fs.copyFile(polyfillSrc, path.join(cssJsDistDir, polyfillDest));
+
+ // Minify and copy polyfill
+ const cliPolyfillMinDest = path.join(cliJsDistDir, polyfillMinDest);
+ await execPromise(`npx terser ${polyfillSrc} -o ${cliPolyfillMinDest} --compress --mangle`);
+ await fs.copyFile(cliPolyfillMinDest, path.join(cssJsDistDir, polyfillMinDest));
+
+ // Create combined component files
+ console.log('Creating combined component files...');
+ const componentsToCombine = ['dropdown-menu.js', 'select.js', 'sidebar.js', 'tabs.js', 'toast.js'];
+ const componentPaths = componentsToCombine.map(f => path.join(srcJsDir, f));
+
+ // Create non-minified bundle
+ let combinedContent = '';
+ for (const p of componentPaths) {
+ combinedContent += await fs.readFile(p, 'utf-8') + '\n';
+ }
+ const allJsPath = path.join(cliJsDistDir, 'all.js');
+ await fs.writeFile(allJsPath, combinedContent);
+ await fs.copyFile(allJsPath, path.join(cssJsDistDir, 'all.js'));
+
+ // Create minified bundle
+ const allMinJsPath = path.join(cliJsDistDir, 'all.min.js');
+ await execPromise(`npx terser ${componentPaths.join(' ')} -o ${allMinJsPath} --compress --mangle`);
+ await fs.copyFile(allMinJsPath, path.join(cssJsDistDir, 'all.min.js'));
+
+ console.log(`Copied and minified JS to ${cliJsDistDir} and ${cssJsDistDir}`);
+
await copyDirRecursive(srcNunjucksDir, path.join(cliDistAssetsDir, 'nunjucks'));
console.log(`Copied Nunjucks assets to ${path.join(cliDistAssetsDir, 'nunjucks')}`);
await copyDirRecursive(srcJinjaDir, path.join(cliDistAssetsDir, 'jinja'));
@@ -103,6 +165,16 @@ async function build() {
await fs.copyFile(path.join(srcCssDir, 'basecoat.css'), path.join(cssDistDir, 'basecoat.css'));
console.log(`Copied basecoat.css to ${cssDistDir}`);
+ // Create Tailwind CSS builds for the CSS package
+ const cdnCssSrc = path.join(srcCssDir, 'cdn.css');
+ const cssDistCdnPath = path.join(cssDistDir, 'cdn.css');
+ const cssDistCdnMinPath = path.join(cssDistDir, 'cdn.min.css');
+
+ await execPromise(`npx tailwindcss -i "${cdnCssSrc}" -o "${cssDistCdnPath}"`);
+ console.log(`Generated non-minified CSS: ${cssDistCdnPath}`);
+ await execPromise(`npx tailwindcss -i "${cdnCssSrc}" -o "${cssDistCdnMinPath}" --minify`);
+ console.log(`Generated minified CSS: ${cssDistCdnMinPath}`);
+
console.log('Build process finished successfully!');
}