{{ title }}
+{{ title }}
{{ description }}
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..0efdbe2 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [hunvreus] diff --git a/.gitignore b/.gitignore index 3a21961..980c67e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,11 +2,10 @@ !.gitignore !.eleventy.js -/node_modules -/_site - +_site/ +node_modules/ +packages/cli/dist/ +packages/css/dist/ # Logs -npm-debug.log* -yarn-debug.log* -yarn-error.log* \ No newline at end of file +npm-debug.log* \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md index 63b4b68..5ba264e 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ MIT License -Copyright (c) [year] [fullname] +Copyright (c) 2025 Ronan Berder Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 474d404..89af2ac 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ A collection of modern UI components built with Tailwind CSS that works with any web stack. Basecoat brings the magic of [shadcn/ui](https://ui.shadcn.com) to traditional web applications, no React required. + + ## Features - **Lightweight**: no runtime JS, just CSS and tiny Alpine.js scripts for the more interactive components. diff --git a/cli/index.js b/cli/index.js deleted file mode 100755 index 21e913e..0000000 --- a/cli/index.js +++ /dev/null @@ -1,175 +0,0 @@ -#!/usr/bin/env node - -import { Command } from 'commander'; -import inquirer from 'inquirer'; -import fs from 'fs-extra'; -import path from 'path'; - -const program = new Command(); - -let packageVersion = 'unknown'; -try { - const cliScriptDir = path.dirname(new URL(import.meta.url).pathname); - const packageJsonPath = path.resolve(cliScriptDir, '..', 'package.json'); - const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf8'); - packageVersion = JSON.parse(packageJsonContent).version; -} catch (error) { - console.warn('Could not read package version:', error.message); -} - -const DEFAULT_CONFIG = { - templateEngine: null, - templateDest: './components/basecoat', - scriptDest: './static/js/basecoat' -}; -let config = { ...DEFAULT_CONFIG }; - -async function getAvailableComponents() { - try { - const packageRoot = path.resolve(path.dirname(new URL(import.meta.url).pathname), '..'); - const jsSourceDir = path.join(packageRoot, 'src', 'js'); - const jsFiles = await fs.readdir(jsSourceDir); - return jsFiles - .filter(file => file.endsWith('.js')) - .map(file => path.basename(file, '.js')); - } catch (error) { - console.error("Error reading component source directories:", error); - return []; - } -} - -async function ensureConfiguration() { - console.log('\nChecking configuration...'); - - if (!config.templateEngine) { - const engineChoice = await inquirer.prompt([ - { - type: 'list', - name: 'engine', - message: 'Which template engine are you using?', - choices: ['nunjucks', 'jinja'], - } - ]); - config.templateEngine = engineChoice.engine; - } - - const pathChoices = await inquirer.prompt([ - { - type: 'input', - name: 'templateDest', - message: 'Where should template files be placed?', - default: './', - }, - { - type: 'input', - name: 'scriptDest', - message: 'Where should script files be placed?', - default: './', - } - ]); - config.templateDest = pathChoices.templateDest; - config.scriptDest = pathChoices.scriptDest; - - console.log(`Configuration set: Engine=${config.templateEngine}, Templates=${config.templateDest}, Scripts=${config.scriptDest}`); -} - -console.log('Basecoat CLI'); - -program - .name('basecoat') - .description('Add Basecoat components to your project') - .version(packageVersion); - -program - .command('add') - .description('Add one or more components to your project') - .argument('[components...]', 'Names of components to add') - .action(async (componentsArg) => { - let componentsToAdd = componentsArg; - try { - if (!componentsToAdd || componentsToAdd.length === 0) { - const availableComponents = await getAvailableComponents(); - if (availableComponents.length === 0) { - console.error('Error: Could not find any components.'); - return; - } - - const answers = await inquirer.prompt([ - { - type: 'checkbox', - name: 'selectedComponents', - message: 'Which component(s) would you like to add?', - choices: availableComponents, - validate: (input) => input.length > 0 ? true : 'Please select at least one component.' - } - ]); - - componentsToAdd = answers.selectedComponents; - } - - if (!componentsToAdd || componentsToAdd.length === 0) { - console.log('No components selected. Exiting.'); - return; - } - - console.log(`Attempting to add: ${componentsToAdd.join(', ')}`); - - await ensureConfiguration(); - - for (const componentName of componentsToAdd) { - await addComponent(componentName); - } - - console.log('\nComponent addition process finished.'); - } catch (error) { - if (error.isTtyError || error.constructor.name === 'ExitPromptError') { - console.log('\nOperation cancelled.'); - } else { - console.error('\nAn unexpected error occurred:', error); - } - process.exit(1); - } - }); - -async function addComponent(componentName) { - console.log(`\nProcessing component: ${componentName}...`); - - // 1. Determine source paths (relative to package root) - const cliScriptDir = path.dirname(new URL(import.meta.url).pathname); // Get directory of the current script file - // Go UP from cli/, then DOWN into src/, then the specific folder - const templateExt = config.templateEngine === 'jinja' ? '.html.jinja' : '.njk'; - const templateSource = path.resolve(cliScriptDir, '..', 'src', config.templateEngine, `${componentName}${templateExt}`); - const scriptSource = path.resolve(cliScriptDir, '..', 'src', 'js', `${componentName}.js`); - - // 2. Determine destination paths (relative to user's current working directory) - const templateDestPath = path.resolve(process.cwd(), config.templateDest, `${componentName}${templateExt}`); - const scriptDestPath = path.resolve(process.cwd(), config.scriptDest, `${componentName}.js`); - - try { - // 3. Check if source files exist - const templateExists = await fs.pathExists(templateSource); - const scriptExists = await fs.pathExists(scriptSource); - - if (!templateExists || !scriptExists) { - console.error(` Error: Source files for component '${componentName}' not found. Searched:`); - if (!templateExists) console.error(` - ${templateSource}`); - if (!scriptExists) console.error(` - ${scriptSource}`); - return; // Skip this component - } - - // 4. Ensure destination directories exist - await fs.ensureDir(path.dirname(templateDestPath)); - await fs.ensureDir(path.dirname(scriptDestPath)); - - // 5. Copy files - await fs.copyFile(templateSource, templateDestPath); - console.log(` -> Copied template to: ${templateDestPath}`); - await fs.copyFile(scriptSource, scriptDestPath); - console.log(` -> Copied script to: ${scriptDestPath}`); - - } catch (error) { - console.error(` Error processing component ${componentName}:`, error); - } -} - -program.parse(process.argv); \ No newline at end of file diff --git a/docs/css/styles.css b/docs/css/styles.css index 3cff2a4..b478c73 100644 --- a/docs/css/styles.css +++ b/docs/css/styles.css @@ -1,16 +1,13 @@ -@import url("https://fonts.googleapis.com/css2?family=Poppins&display=swap"); -@import url("https://fonts.googleapis.com/css2?family=Lora&display=swap"); -@import url("https://fonts.googleapis.com/css2?family=Fira+Code&display=swap"); -@import url("https://fonts.googleapis.com/css2?family=JetBrains+Mono&display=swap"); -@import url("https://fonts.googleapis.com/css2?family=Inter&display=swap"); +@import url("https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&display=swap"); +@import url("https://fonts.googleapis.com/css2?family=Geist+Mono&display=swap"); @import "tailwindcss"; @import "../../src/css/basecoat.css"; @import "./custom.css"; @theme { - --font-sans: 'Inter'; - --font-mono: 'JetBrains Mono'; + --font-sans: 'Geist'; + --font-mono: 'Geist Mono'; } @import "./themes/claude.css"; diff --git a/docs/src/_data/site.js b/docs/src/_data/site.js new file mode 100644 index 0000000..ee150c2 --- /dev/null +++ b/docs/src/_data/site.js @@ -0,0 +1,10 @@ +export default { + title: "Basecoat", + description: "A components library built with Tailwind CSS that works with any web stack.", + keywords: ["components", "component library", "component system", "UI", "UI kit", "shadcn", "shadcn/ui", "Tailwind CSS", "Tailwind", "CSS", "HTML", "Jinja", "Nunjucks", "Alpine.js"], + author: { + name: "Ronan Berder", + x: "@hunvreus" + }, + url: process.env.ELEVENTY_ENV === 'development' ? "http://localhost:8080" : "https://basecoatui.com" +}; \ No newline at end of file diff --git a/docs/src/_data/site.json b/docs/src/_data/site.json deleted file mode 100644 index 05d4ae9..0000000 --- a/docs/src/_data/site.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Basecoat", - "description": "shadcn/ui for the rest of us" -} \ No newline at end of file diff --git a/docs/src/_includes/layouts/base.njk b/docs/src/_includes/layouts/base.njk index 171328f..2440f99 100644 --- a/docs/src/_includes/layouts/base.njk +++ b/docs/src/_includes/layouts/base.njk @@ -45,18 +45,36 @@ -
{{ description }}
You can include the Javascript code provided below, load it as an individual file or use the CLI. Some Alpine.js properties are also required on certain elements (e.g. x-bind, x-data, @click).
{% endset %}
diff --git a/docs/src/components/breadcrumb.njk b/docs/src/components/breadcrumb.njk
index 2f28cef..106c792 100644
--- a/docs/src/components/breadcrumb.njk
+++ b/docs/src/components/breadcrumb.njk
@@ -75,7 +75,7 @@ document.addEventListener('alpine:init', () => {
You can include the Javascript code provided below, load it as an individual file or use the CLI. Some Alpine.js properties are also required on certain elements (e.g. x-bind, x-data, @click).
You can include the Javascript code provided below, load it as an individual file or use the CLI. Some Alpine.js properties are also required on certain elements (e.g. x-bind, x-data, @click).
The component will dispatch dialog:opened and dialog:closed events when the dialog is opened or closed. You can also dispatch dialog:open and dialog:close events to open or close the dialog programmatically. You will need the element to have an id to be able to target it (e.g. <div id="my-dialog" class="dialog">):
You can use the dialog() Nunjucks or Jinja macro for this component.
You can include the Javascript code provided below, load it as an individual file or use the CLI. Some Alpine.js properties are also required on certain elements (e.g. x-bind, x-data, @click).
You can include the Javascript code provided below, load it as an individual file or use the CLI. Some Alpine.js properties are also required on certain elements (e.g. x-bind, x-data, @click).
You can include the Javascript code provided below, load it as an individual file or use the CLI. Some Alpine.js properties are also required on certain elements (e.g. x-bind, x-data, @click).
The component will dispatch a select:change event when a value is selected, along with the value and label of the selected option in the detail object.
You can use the dropdown_menu() Nunjucks or Jinja macro for this component.
You can include the Javascript code provided below, load it as an individual file or use the CLI. Some Alpine.js properties are also required on certain elements (e.g. x-bind, x-data, @click).
You can include the Javascript code provided below, load it as an individual file or use the CLI. Some Alpine.js properties are also required on certain elements (e.g. x-bind, x-data, @click).
You will also need to include the Javascript code provided below, load it as an individual file or use the CLI. Some Alpine.js properties are also required on certain elements (e.g. x-bind, x-data, @click).
A components library built with Tailwind CSS that works with any web stack.
Add Basecoat to your Tailwind CSS file.
Alternatively, you can directly copy the basecoat.css file into your project.
If you're using Nunjucks or Jinja, you can use the CLI to install the CSS and macros for any of the complex components(e.g. Dialog). It will copy the JS and `.html.jinja or .njk macros into your code.
For example, to add the Dialog component, run:
- {% set code %}npx basecoatui add dialog{% endset %} - {{ code_block(code, "bash") }} +If you're using Nunjucks or Jinja, you can use the CLI to install the CSS and macros for any of the complex components(e.g. Dialog). It will copy the JS and .html.jinja or .njk macros into your code.
For example, to add the Dialog component, run one of the following commands:
+You will be asked to confirm the template engine and destination directory for the Javascript and macros.
You can override default styles using Tailwind:
+ {% set code %}{% endset %} + {{ code_block(code, "html") }} +You can also obviously extend and/or override the existing styles with 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.
There's also a CLI to help you scaffold components and get set up fast.
-Check out the installation guide to get started.
+Check out the installation guide to get started.
Basecoat is 100% open source and free.
-{{ description }}
+{{ description }}
+ {% endif %} +